コード例 #1
0
ファイル: main.py プロジェクト: TanPhuNguyen/Face-Recognition
    def __init__(self, parent=None):
        super(Camera, self).__init__(parent)
        self.ui = Ui_Camera()
        self.pre_id = 0
        self.cur_id = 0
        self.count = 0
        self.checked = 0
        self.audio_settime = 0
        self.allow_flag = 1
        self.check_list = []
        self.camera = None
        self.imageCapture = None
        self.isCapturingImage = False
        self.applicationExiting = False
        self.ui.setupUi(self)
        cameraDevice = QByteArray()
        videoDevicesGroup = QActionGroup(self)
        videoDevicesGroup.setExclusive(True)
        for deviceName in QCamera.availableDevices():
            description = QCamera.deviceDescription(deviceName)
            videoDeviceAction = QAction(description, videoDevicesGroup)
            videoDeviceAction.setCheckable(True)
            videoDeviceAction.setData(deviceName)
            if cameraDevice.isEmpty():
                cameraDevice = deviceName
                videoDeviceAction.setChecked(True)
            self.ui.menuDevices.addAction(videoDeviceAction)
        videoDevicesGroup.triggered.connect(self.updateCameraDevice)
        self.setCamera(cameraDevice)

        # Create and load model
        path_pretrained = "apis/models/facenet/20180402-114759.pb"
        path_SVM = "apis/models/SVM/SVM.pkl"
        self.recognizer = Recognizer()
        self.recognizer.create_graph(path_pretrained, path_SVM)
コード例 #2
0
    def __init__(self, parent=None, standalone=False):
        super(Camera, self).__init__(parent)

        # This prevents doing unneeded initialization
        # when QtDesginer loads the plugin.
        if parent is None and not standalone:
            return

        if not multimedia_available:
            return

        self.ui = uic.loadUi(os.path.join(WIDGET_PATH, "camera.ui"), self)

        self.camera = None
        self.imageCapture = None
        self.mediaRecorder = None
        self.isCapturingImage = False
        self.applicationExiting = False

        self.imageSettings = QImageEncoderSettings()
        self.audioSettings = QAudioEncoderSettings()
        self.videoSettings = QVideoEncoderSettings()
        self.videoContainerFormat = ''

        camera_device = QByteArray()

        videoDevicesGroup = QActionGroup(self)

        videoDevicesGroup.setExclusive(True)

        if not QCamera.availableDevices():
            self.ui.devicesCombo.addItem("No Device")
        else:
            for deviceName in QCamera.availableDevices():
                description = QCamera.deviceDescription(deviceName)
                self.ui.devicesCombo.addItem(description)

                videoDeviceAction = QAction(description, videoDevicesGroup)
                videoDeviceAction.setCheckable(True)
                videoDeviceAction.setData(deviceName)

                if camera_device.isEmpty():
                    cameraDevice = deviceName
                    videoDeviceAction.setChecked(True)

                self.ui.devicesCombo.addAction(videoDeviceAction)

        videoDevicesGroup.triggered.connect(self.updateCameraDevice)

        self.ui.captureWidget.currentChanged.connect(self.updateCaptureMode)

        self.ui.devicesCombo.currentIndexChanged.connect(
            self.get_device_action)

        self.ui.lockButton.hide()

        # Start camera 2s after the UI has loaded
        QTimer.singleShot(2000, lambda: self.setCamera(camera_device))
コード例 #3
0
  def setup_camera(self):
    camera_device = QByteArray()

    for device in QCamera.availableDevices():
      if camera_device.isEmpty():
        camera_device = device
    if camera_device.isEmpty():
      self.camera = QCamera()
    else:
      self.camera = QCamera(camera_device)

    self.image_capture = QCameraImageCapture(self.camera)
    self.image_capture.readyForCaptureChanged.connect(self.ready_for_capture)
    self.image_capture.imageCaptured.connect(self.process_captured_image)
    self.image_capture.imageSaved.connect(self.image_saved)

    self.camera.setViewfinder(self.ui.viewFinder)
    self.camera.start()
コード例 #4
0
    def setup_camera(self):
        camera_device = QByteArray()

        video_devices_group = QActionGroup(self)
        video_devices_group.setExclusive(True)

        for device in QCamera.availableDevices():
            description = QCamera.deviceDescription(device)
            video_device_action = QAction(description, video_devices_group)
            video_device_action.setCheckable(True)
            video_device_action.setData(device)

            if camera_device.isEmpty():
                camera_device = device
                video_device_action.setChecked(True)

            self.ui.menuDevices.addAction(video_device_action)
        if camera_device.isEmpty():
            self.camera = QCamera()
        else:
            self.camera = QCamera(camera_device)
        self.camera.setViewfinder(self.ui.cameraViewFinder)
コード例 #5
0
    def __init__(self, parent=None):
        super(Camera, self).__init__(parent)
        global API
        API = AlgorithmAPIs(template_dir="templates",
                            threshold=0.5,
                            use_multiprocessing=False)

        self.ui = Ui_Camera()

        self.camera = None
        self.imageCapture = None
        # self.mediaRecorder = None
        self.isCapturingImage = False
        self.applicationExiting = False

        self.imageSettings = QImageEncoderSettings()
        self.audioSettings = QAudioEncoderSettings()
        self.ui.setupUi(self)
        cameraDevice = QByteArray()
        videoDevicesGroup = QActionGroup(self)
        videoDevicesGroup.setExclusive(True)

        for deviceName in QCamera.availableDevices():
            description = QCamera.deviceDescription(deviceName)
            videoDeviceAction = QAction(description, videoDevicesGroup)
            videoDeviceAction.setCheckable(True)
            videoDeviceAction.setData(deviceName)

            if cameraDevice.isEmpty():
                cameraDevice = deviceName
                videoDeviceAction.setChecked(True)

            self.ui.menuDevices.addAction(videoDeviceAction)

        videoDevicesGroup.triggered.connect(self.updateCameraDevice)

        self.ui.lcdNumber_2.display(0)

        self.ui.dial.valueChanged.connect(self.dial_display)

        global dial_value
        dial_value = 3
        self.ui.lcdNumber_2.display(dial_value)
        self.setCamera(cameraDevice)

        # Create and load model
        path_pretrained = "apis/models/facenet/20180402-114759.pb"
        path_SVM = "apis/models/SVM/SVM.pkl"
        self.recognizer = Recognizer()
        self.recognizer.create_graph(path_pretrained, path_SVM)
コード例 #6
0
    def detectDevices(self):
        cameraDevice = QByteArray()

        for name in QCamera.availableDevices():
            description = QCamera.deviceDescription(name)
            deviceAction = QAction(description, self.videoDevicesGroup)
            deviceAction.setCheckable(True)
            deviceAction.setData(name)

            if cameraDevice.isEmpty():
                cameraDevice = name
                deviceAction.setChecked(True)

            self.menuDevices.addAction(deviceAction)

        self.videoDevicesGroup.triggered.connect(self.changeCameraDevice)
        self.setCamera(cameraDevice)
コード例 #7
0
ファイル: tomarFoto.py プロジェクト: ykv001/PyQt5
    def initUI(self):

        # ========================= MENÚ ===========================

        menu = self.menuBar()

        archivoMenu = menu.addMenu("&Archivo")
        self.accionIniciarCamara = archivoMenu.addAction(
            self.style().standardIcon(QStyle.SP_MediaPlay),
            "Iniciar la camara", self.iniciarCamara)
        self.accionDetenerCamara = archivoMenu.addAction(
            self.style().standardIcon(QStyle.SP_MediaStop),
            "Detener la camara", self.detenerCamara)
        archivoMenu.addAction(
            self.style().standardIcon(QStyle.SP_MessageBoxCritical), " Salir",
            self.close)

        dispositivosMenu = menu.addMenu("&Dispositivos")

        videoDevicesGroup = QActionGroup(self)
        videoDevicesGroup.setExclusive(True)

        dispositivoCamara = QByteArray()

        for nombreDispositivo in QCamera.availableDevices():
            descripcion = QCamera.deviceDescription(nombreDispositivo)
            videoDeviceAction = QAction(descripcion, videoDevicesGroup)
            videoDeviceAction.setCheckable(True)
            videoDeviceAction.setData(nombreDispositivo)

            if dispositivoCamara.isEmpty():
                dispositivoCamara = nombreDispositivo
                videoDeviceAction.setChecked(True)

            dispositivosMenu.addAction(videoDeviceAction)

        # Instancia del Widget central
        self.widgets = Widgets(dispositivoCamara, self)

        # Llamar función cuando se activa una Acción del Menú
        videoDevicesGroup.triggered.connect(
            self.widgets.actualizarDispositivoCamara)

        # Establecer el Widget central de la ventana
        self.setCentralWidget(self.widgets)
コード例 #8
0
    def __init__(self, arg):
        super(Camera, self).__init__(parent)
        # self.arg = arg
        # Attributes variables
        self.ui = Ui_Camera()
        self.camera = None
        self.imageCapture = None
        self.mediaRecorder = None
        self.isCapturingImage = False
        self.applicationExiting = False

        self.imageSettings = QImageEncoderSettings()
        self.audioSettings = QAudioEncoderSettings()
        self.videoSettings = QVideoEncoderSettings()
        self.videoContainerFormat = ''

        self.ui.setupUi(self)

        #get device camera
        cameraDevice = QByteArray()

        videoDevicesGroup = QActionGroup(self)
        videoDevicesGroup.setExclusive(True)

        # Get informations about available cameras
        for deviceName in QCamera.availableDevices():
            description = QCamera.deviceDescription(deviceName)
            videoDeviceAction = QAction(description, videoDevicesGroup)
            videoDeviceAction.setCheckable(True)
            videoDeviceAction.setData(deviceName)

            if cameraDevice.isEmpty():
                cameraDevice = deviceName
                videoDeviceAction.setChecked(True)

            self.ui.menuDevices.addAction(videoDeviceAction)

        videoDevicesGroup.triggered.connect(self.updateCameraDevice)
        self.ui.captureWidget.currentChanged.connect(self.updateCaptureMode)

        self.ui.lockButton.hide()

        self.setCamera(cameraDevice)
コード例 #9
0
    def __init__(self, parent=None):
        super(Camera, self).__init__(parent)

        self.ui = Ui_Camera()
        self.camera = None
        self.imageCapture = None
        self.mediaRecorder = None
        self.isCapturingImage = False
        self.applicationExiting = False

        self.imageSettings = QImageEncoderSettings()
        self.audioSettings = QAudioEncoderSettings()
        self.videoSettings = QVideoEncoderSettings()
        self.videoContainerFormat = ''

        self.ui.setupUi(self)

        cameraDevice = QByteArray()

        videoDevicesGroup = QActionGroup(self)
        videoDevicesGroup.setExclusive(True)

#for multiple cameras ata

        for deviceName in QCamera.availableDevices():
            description = QCamera.deviceDescription(deviceName)
            videoDeviceAction = QAction(description, videoDevicesGroup)
            videoDeviceAction.setCheckable(True)
            videoDeviceAction.setData(deviceName)

            if cameraDevice.isEmpty():
                cameraDevice = deviceName
                videoDeviceAction.setChecked(True)

            self.ui.menuDevices.addAction(videoDeviceAction)

        videoDevicesGroup.triggered.connect(self.updateCameraDevice)
        self.ui.captureWidget.currentChanged.connect(self.updateCaptureMode)

        self.ui.lockButton.hide()

        self.setCamera(cameraDevice)
コード例 #10
0
class MainForm(QDialog):

    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)

        self.filename = ""
        self.copiedItem = QByteArray()
        self.pasteOffset = 5
        self.prevPoint = QPoint()
        self.addOffset = 5
        self.borders = []

        self.printer = QPrinter(QPrinter.HighResolution)
        self.printer.setPageSize(QPrinter.Letter)

        self.view = GraphicsView()
        self.scene = QGraphicsScene(self)
        self.scene.setSceneRect(0, 0, PageSize[0], PageSize[1])
        self.addBorders()
        self.view.setScene(self.scene)

        self.wrapped = [] # Needed to keep wrappers alive
        buttonLayout = QVBoxLayout()
        for text, slot in (
                ("Add &Text", self.addText),
                ("Add &Box", self.addBox),
                ("Add Pi&xmap", self.addPixmap),
                ("&Align", None),
                ("&Copy", self.copy),
                ("C&ut", self.cut),
                ("&Paste", self.paste),
                ("&Delete...", self.delete),
                ("&Rotate", self.rotate),
                ("Pri&nt...", self.print_),
                ("&Open...", self.open),
                ("&Save", self.save),
                ("&Quit", self.accept)):
            button = QPushButton(text)
            if not MAC:
                button.setFocusPolicy(Qt.NoFocus)
            if slot is not None:
                button.clicked.connect(slot)
            if text == "&Align":
                menu = QMenu(self)
                for text, arg in (
                        ("Align &Left", Qt.AlignLeft),
                        ("Align &Right", Qt.AlignRight),
                        ("Align &Top", Qt.AlignTop),
                        ("Align &Bottom", Qt.AlignBottom)):
                    wrapper = functools.partial(self.setAlignment, arg)
                    self.wrapped.append(wrapper)
                    menu.addAction(text, wrapper)
                button.setMenu(menu)
            if text == "Pri&nt...":
                buttonLayout.addStretch(5)
            if text == "&Quit":
                buttonLayout.addStretch(1)
            buttonLayout.addWidget(button)
        buttonLayout.addStretch()

        layout = QHBoxLayout()
        layout.addWidget(self.view, 1)
        layout.addLayout(buttonLayout)
        self.setLayout(layout)

        fm = QFontMetrics(self.font())
        self.resize(self.scene.width() + fm.width(" Delete... ") + 50,
                    self.scene.height() + 50)
        self.setWindowTitle("Page Designer")


    def addBorders(self):
        self.borders = []
        rect = QRectF(0, 0, PageSize[0], PageSize[1])
        self.borders.append(self.scene.addRect(rect, Qt.yellow))
        margin = 5.25 * PointSize
        self.borders.append(self.scene.addRect(
                rect.adjusted(margin, margin, -margin, -margin),
                Qt.yellow))


    def removeBorders(self):
        while self.borders:
            item = self.borders.pop()
            self.scene.removeItem(item)
            del item


    def reject(self):
        self.accept()


    def accept(self):
        self.offerSave()
        QDialog.accept(self)


    def offerSave(self):
        if (Dirty and QMessageBox.question(self,
                            "Page Designer - Unsaved Changes",
                            "Save unsaved changes?",
                            QMessageBox.Yes|QMessageBox.No) == 
           QMessageBox.Yes):
            self.save()


    def position(self):
        point = self.mapFromGlobal(QCursor.pos())
        if not self.view.geometry().contains(point):
            coord = random.randint(36, 144)
            point = QPoint(coord, coord)
        else:
            if point == self.prevPoint:
                point += QPoint(self.addOffset, self.addOffset)
                self.addOffset += 5
            else:
                self.addOffset = 5
                self.prevPoint = point
        return self.view.mapToScene(point)


    def addText(self):
        dialog = TextItemDlg(position=self.position(),
                             scene=self.scene, parent=self)
        dialog.exec_()


    def addBox(self):
        BoxItem(self.position(), self.scene)


    def addPixmap(self):
        path = (QFileInfo(self.filename).path()
            if self.filename else ".")
        fname,filetype = QFileDialog.getOpenFileName(self,
                "Page Designer - Add Pixmap", path,
                "Pixmap Files (*.bmp *.jpg *.png *.xpm)")
        if not fname:
            return
        self.createPixmapItem(QPixmap(fname), self.position())


    def createPixmapItem(self, pixmap, position, matrix=QTransform()):
        item = GraphicsPixmapItem(pixmap)
        item.setFlags(QGraphicsItem.ItemIsSelectable|
                      QGraphicsItem.ItemIsMovable)
        item.setPos(position)
        item.setTransform(matrix)
        self.scene.clearSelection()
        self.scene.addItem(item)
        item.setSelected(True)
        global Dirty
        Dirty = True
        return item


    def selectedItem(self):
        items = self.scene.selectedItems()
        if len(items) == 1:
            return items[0]
        return None


    def copy(self):
        item = self.selectedItem()
        if item is None:
            return
        self.copiedItem.clear()
        self.pasteOffset = 5
        stream = QDataStream(self.copiedItem, QIODevice.WriteOnly)
        self.writeItemToStream(stream, item)


    def cut(self):
        item = self.selectedItem()
        if item is None:
            return
        self.copy()
        self.scene.removeItem(item)
        del item


    def paste(self):
        if self.copiedItem.isEmpty():
            return
        stream = QDataStream(self.copiedItem, QIODevice.ReadOnly)
        self.readItemFromStream(stream, self.pasteOffset)
        self.pasteOffset += 5


    def setAlignment(self, alignment):
        # Items are returned in arbitrary order
        items = self.scene.selectedItems()
        if len(items) <= 1:
            return
        # Gather coordinate data
        leftXs, rightXs, topYs, bottomYs = [], [], [], []
        for item in items:
            rect = item.sceneBoundingRect()
            leftXs.append(rect.x())
            rightXs.append(rect.x() + rect.width())
            topYs.append(rect.y())
            bottomYs.append(rect.y() + rect.height())
        # Perform alignment
        if alignment == Qt.AlignLeft:
            xAlignment = min(leftXs)
            for i, item in enumerate(items):
                item.moveBy(xAlignment - leftXs[i], 0)
        elif alignment == Qt.AlignRight:
            xAlignment = max(rightXs)
            for i, item in enumerate(items):
                item.moveBy(xAlignment - rightXs[i], 0)
        elif alignment == Qt.AlignTop:
            yAlignment = min(topYs)
            for i, item in enumerate(items):
                item.moveBy(0, yAlignment - topYs[i])
        elif alignment == Qt.AlignBottom:
            yAlignment = max(bottomYs)
            for i, item in enumerate(items):
                item.moveBy(0, yAlignment - bottomYs[i])
        global Dirty
        Dirty = True


    def rotate(self):
        for item in self.scene.selectedItems():
            item.setRotation(item.rotation()+30)

    def delete(self):
        items = self.scene.selectedItems()
        if (len(items) and QMessageBox.question(self,
                "Page Designer - Delete",
                "Delete {0} item{1}?".format(len(items),
                "s" if len(items) != 1 else ""),
                QMessageBox.Yes|QMessageBox.No) ==
                QMessageBox.Yes):
            while items:
                item = items.pop()
                self.scene.removeItem(item)
                del item
            global Dirty
            Dirty = True


    def print_(self):
        dialog = QPrintDialog(self.printer)
        if dialog.exec_():
            painter = QPainter(self.printer)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setRenderHint(QPainter.TextAntialiasing)
            self.scene.clearSelection()
            self.removeBorders()
            self.scene.render(painter)
            self.addBorders()


    def open(self):
        self.offerSave()
        path = (QFileInfo(self.filename).path()
                if self.filename else ".")
        fname,filetype = QFileDialog.getOpenFileName(self,
                "Page Designer - Open", path,
                "Page Designer Files (*.pgd)")
        if not fname:
            return
        self.filename = fname
        fh = None
        try:
            fh = QFile(self.filename)
            if not fh.open(QIODevice.ReadOnly):
                raise IOError(str(fh.errorString()))
            items = self.scene.items()
            while items:
                item = items.pop()
                self.scene.removeItem(item)
                del item
            self.addBorders()
            stream = QDataStream(fh)
            stream.setVersion(QDataStream.Qt_5_7)
            magic = stream.readInt32()
            if magic != MagicNumber:
                raise IOError("not a valid .pgd file")
            fileVersion = stream.readInt16()
            if fileVersion != FileVersion:
                raise IOError("unrecognised .pgd file version")
            while not fh.atEnd():
                self.readItemFromStream(stream)
        except IOError as e:
            QMessageBox.warning(self, "Page Designer -- Open Error",
                    "Failed to open {0}: {1}".format(self.filename, e))
        finally:
            if fh is not None:
                fh.close()
        global Dirty
        Dirty = False


    def save(self):
        if not self.filename:
            path = "."
            fname,filetype = QFileDialog.getSaveFileName(self,
                    "Page Designer - Save As", path,
                    "Page Designer Files (*.pgd)")
            if not fname:
                return
            self.filename = fname
        fh = None
        try:
            fh = QFile(self.filename)
            if not fh.open(QIODevice.WriteOnly):
                raise IOError(str(fh.errorString()))
            self.scene.clearSelection()
            stream = QDataStream(fh)
            stream.setVersion(QDataStream.Qt_5_7)
            stream.writeInt32(MagicNumber)
            stream.writeInt16(FileVersion)
            for item in self.scene.items():
                self.writeItemToStream(stream, item)
        except IOError as e:
            QMessageBox.warning(self, "Page Designer -- Save Error",
                    "Failed to save {0}: {1}".format(self.filename, e))
        finally:
            if fh is not None:
                fh.close()
        global Dirty
        Dirty = False


    def readItemFromStream(self, stream, offset=0):
        type = ""
        position = QPointF()
        matrix = QTransform()
        rotateangle=0#add by yangrongdong
        type=stream.readQString()
        stream >> position >> matrix
        if offset:
            position += QPointF(offset, offset)
        if type == "Text":
            text = ""
            font = QFont()
            text=stream.readQString()
            stream >> font
            rotateangle=stream.readFloat()
            tx=TextItem(text, position, self.scene, font, matrix)
            tx.setRotation(rotateangle)
        elif type == "Box":
            rect = QRectF()
            stream >> rect
            style = Qt.PenStyle(stream.readInt16())
            rotateangle=stream.readFloat()
            bx=BoxItem(position, self.scene, style, rect, matrix)
            bx.setRotation(rotateangle)
        elif type == "Pixmap":
            pixmap = QPixmap()
            stream >> pixmap
            rotateangle=stream.readFloat()
            px=self.createPixmapItem(pixmap, position, matrix)
            px.setRotation(rotateangle)


    def writeItemToStream(self, stream, item):
        if isinstance(item, TextItem):
            stream.writeQString("Text")
            stream<<item.pos()<< item.transform() 
            stream.writeQString(item.toPlainText())
            stream<< item.font()
            stream.writeFloat(item.rotation())#add by yangrongdong
        elif isinstance(item, GraphicsPixmapItem):
            stream.writeQString("Pixmap")
            stream << item.pos() << item.transform() << item.pixmap()
            stream.writeFloat(item.rotation())#add by yangrongdong
        elif isinstance(item, BoxItem):
            stream.writeQString("Box")
            stream<< item.pos() << item.transform() << item.rect
            stream.writeInt16(item.style)
            stream.writeFloat(item.rotation())#add by yangrongdong
コード例 #11
0
class AdBlockSubscription(QObject):
    """
    Class implementing the AdBlock subscription.
    
    @signal changed() emitted after the subscription has changed
    @signal rulesChanged() emitted after the subscription's rules have changed
    @signal enabledChanged(bool) emitted after the enabled state was changed
    """
    changed = pyqtSignal()
    rulesChanged = pyqtSignal()
    enabledChanged = pyqtSignal(bool)

    def __init__(self, url, custom, parent=None, default=False):
        """
        Constructor
        
        @param url AdBlock URL for the subscription (QUrl)
        @param custom flag indicating a custom subscription (boolean)
        @param parent reference to the parent object (QObject)
        @param default flag indicating a default subscription (boolean)
        """
        super(AdBlockSubscription, self).__init__(parent)

        self.__custom = custom
        self.__url = url.toEncoded()
        self.__enabled = False
        self.__downloading = None
        self.__defaultSubscription = default

        self.__title = ""
        self.__location = QByteArray()
        self.__lastUpdate = QDateTime()
        self.__requiresLocation = ""
        self.__requiresTitle = ""

        self.__updatePeriod = 0  # update period in hours, 0 = use default
        self.__remoteModified = QDateTime()

        self.__rules = []  # list containing all AdBlock rules

        self.__networkExceptionRules = []
        self.__networkBlockRules = []
        self.__domainRestrictedCssRules = []
        self.__elementHidingRules = ""
        self.__documentRules = []
        self.__elemhideRules = []

        self.__checksumRe = re.compile(
            r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""",
            re.IGNORECASE | re.MULTILINE)
        self.__expiresRe = re.compile(
            r"""(?:expires:|expires after)\s*(\d+)\s*(hour|h)?""",
            re.IGNORECASE)
        self.__remoteModifiedRe = re.compile(
            r"""!\s*(?:Last modified|Updated):\s*(\d{1,2})\s*"""
            r"""(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*"""
            r"""(\d{2,4})\s*((\d{1,2}):(\d{2}))?""", re.IGNORECASE)

        self.__monthNameToNumber = {
            "Jan": 1,
            "Feb": 2,
            "Mar": 3,
            "Apr": 4,
            "May": 5,
            "Jun": 6,
            "Jul": 7,
            "Aug": 8,
            "Sep": 9,
            "Oct": 10,
            "Nov": 11,
            "Dec": 12
        }

        self.__parseUrl(url)

    def __parseUrl(self, url):
        """
        Private method to parse the AdBlock URL for the subscription.
        
        @param url AdBlock URL for the subscription (QUrl)
        """
        if url.scheme() != "abp":
            return

        if url.path() != "subscribe":
            return

        if qVersion() >= "5.0.0":
            from PyQt5.QtCore import QUrlQuery
            urlQuery = QUrlQuery(url)
            self.__title = QUrl.fromPercentEncoding(
                QByteArray(urlQuery.queryItemValue("title").encode()))
            self.__enabled = urlQuery.queryItemValue("enabled") != "false"
            self.__location = QByteArray(
                QUrl.fromPercentEncoding(
                    QByteArray(urlQuery.queryItemValue(
                        "location").encode())).encode("utf-8"))

            # Check for required subscription
            self.__requiresLocation = QUrl.fromPercentEncoding(
                QByteArray(
                    urlQuery.queryItemValue("requiresLocation").encode()))
            self.__requiresTitle = QUrl.fromPercentEncoding(
                QByteArray(urlQuery.queryItemValue("requiresTitle").encode()))
            if self.__requiresLocation and self.__requiresTitle:
                import Helpviewer.HelpWindow
                Helpviewer.HelpWindow.HelpWindow.adBlockManager()\
                    .loadRequiredSubscription(self.__requiresLocation,
                                              self.__requiresTitle)

            lastUpdateString = urlQuery.queryItemValue("lastUpdate")
            self.__lastUpdate = QDateTime.fromString(lastUpdateString,
                                                     Qt.ISODate)
        else:
            self.__title = \
                QUrl.fromPercentEncoding(url.encodedQueryItemValue(b"title"))
            self.__enabled = QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"enabled")) != "false"
            self.__location = QByteArray(
                QUrl.fromPercentEncoding(
                    url.encodedQueryItemValue(b"location")).encode("utf-8"))

            # Check for required subscription
            self.__requiresLocation = QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"requiresLocation"))
            self.__requiresTitle = QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"requiresTitle"))
            if self.__requiresLocation and self.__requiresTitle:
                import Helpviewer.HelpWindow
                Helpviewer.HelpWindow.HelpWindow.adBlockManager()\
                    .loadRequiredSubscription(self.__requiresLocation,
                                              self.__requiresTitle)

            lastUpdateByteArray = url.encodedQueryItemValue(b"lastUpdate")
            lastUpdateString = QUrl.fromPercentEncoding(lastUpdateByteArray)
            self.__lastUpdate = QDateTime.fromString(lastUpdateString,
                                                     Qt.ISODate)

        self.__loadRules()

    def url(self):
        """
        Public method to generate the URL for this subscription.
        
        @return AdBlock URL for the subscription (QUrl)
        """
        url = QUrl()
        url.setScheme("abp")
        url.setPath("subscribe")

        queryItems = []
        queryItems.append(("location", bytes(self.__location).decode()))
        queryItems.append(("title", self.__title))
        if self.__requiresLocation and self.__requiresTitle:
            queryItems.append(("requiresLocation", self.__requiresLocation))
            queryItems.append(("requiresTitle", self.__requiresTitle))
        if not self.__enabled:
            queryItems.append(("enabled", "false"))
        if self.__lastUpdate.isValid():
            queryItems.append(
                ("lastUpdate", self.__lastUpdate.toString(Qt.ISODate)))
        if qVersion() >= "5.0.0":
            from PyQt5.QtCore import QUrlQuery
            query = QUrlQuery()
            query.setQueryItems(queryItems)
            url.setQuery(query)
        else:
            url.setQueryItems(queryItems)
        return url

    def isEnabled(self):
        """
        Public method to check, if the subscription is enabled.
        
        @return flag indicating the enabled status (boolean)
        """
        return self.__enabled

    def setEnabled(self, enabled):
        """
        Public method to set the enabled status.
        
        @param enabled flag indicating the enabled status (boolean)
        """
        if self.__enabled == enabled:
            return

        self.__enabled = enabled
        self.enabledChanged.emit(enabled)

    def title(self):
        """
        Public method to get the subscription title.
        
        @return subscription title (string)
        """
        return self.__title

    def setTitle(self, title):
        """
        Public method to set the subscription title.
        
        @param title subscription title (string)
        """
        if self.__title == title:
            return

        self.__title = title
        self.changed.emit()

    def location(self):
        """
        Public method to get the subscription location.
        
        @return URL of the subscription location (QUrl)
        """
        return QUrl.fromEncoded(self.__location)

    def setLocation(self, url):
        """
        Public method to set the subscription location.
        
        @param url URL of the subscription location (QUrl)
        """
        if url == self.location():
            return

        self.__location = url.toEncoded()
        self.__lastUpdate = QDateTime()
        self.changed.emit()

    def requiresLocation(self):
        """
        Public method to get the location of a required subscription.
        
        @return location of a required subscription (string)
        """
        return self.__requiresLocation

    def lastUpdate(self):
        """
        Public method to get the date and time of the last update.
        
        @return date and time of the last update (QDateTime)
        """
        return self.__lastUpdate

    def rulesFileName(self):
        """
        Public method to get the name of the rules file.
        
        @return name of the rules file (string)
        """
        if self.location().scheme() == "file":
            return self.location().toLocalFile()

        if self.__location.isEmpty():
            return ""

        sha1 = bytes(
            QCryptographicHash.hash(self.__location,
                                    QCryptographicHash.Sha1).toHex()).decode()
        dataDir = os.path.join(Utilities.getConfigDir(), "browser",
                               "subscriptions")
        if not os.path.exists(dataDir):
            os.makedirs(dataDir)
        fileName = os.path.join(dataDir,
                                "adblock_subscription_{0}".format(sha1))
        return fileName

    def __loadRules(self):
        """
        Private method to load the rules of the subscription.
        """
        fileName = self.rulesFileName()
        f = QFile(fileName)
        if f.exists():
            if not f.open(QIODevice.ReadOnly):
                E5MessageBox.warning(
                    None, self.tr("Load subscription rules"),
                    self.tr(
                        """Unable to open adblock file '{0}' for reading.""").
                    format(fileName))
            else:
                textStream = QTextStream(f)
                header = textStream.readLine(1024)
                if not header.startswith("[Adblock"):
                    E5MessageBox.warning(
                        None, self.tr("Load subscription rules"),
                        self.tr("""AdBlock file '{0}' does not start"""
                                """ with [Adblock.""").format(fileName))
                    f.close()
                    f.remove()
                    self.__lastUpdate = QDateTime()
                else:
                    from .AdBlockRule import AdBlockRule

                    self.__updatePeriod = 0
                    self.__remoteModified = QDateTime()
                    self.__rules = []
                    self.__rules.append(AdBlockRule(header, self))
                    while not textStream.atEnd():
                        line = textStream.readLine()
                        self.__rules.append(AdBlockRule(line, self))
                        expires = self.__expiresRe.search(line)
                        if expires:
                            period, kind = expires.groups()
                            if kind:
                                # hours
                                self.__updatePeriod = int(period)
                            else:
                                # days
                                self.__updatePeriod = int(period) * 24
                        remoteModified = self.__remoteModifiedRe.search(line)
                        if remoteModified:
                            day, month, year, time, hour, minute = \
                                remoteModified.groups()
                            self.__remoteModified.setDate(
                                QDate(int(year),
                                      self.__monthNameToNumber[month],
                                      int(day)))
                            if time:
                                self.__remoteModified.setTime(
                                    QTime(int(hour), int(minute)))
                    self.__populateCache()
                    self.changed.emit()
        elif not fileName.endswith("_custom"):
            self.__lastUpdate = QDateTime()

        self.checkForUpdate()

    def checkForUpdate(self):
        """
        Public method to check for an update.
        """
        if self.__updatePeriod:
            updatePeriod = self.__updatePeriod
        else:
            updatePeriod = Preferences.getHelp("AdBlockUpdatePeriod") * 24
        if not self.__lastUpdate.isValid() or \
           (self.__remoteModified.isValid() and
            self.__remoteModified.addSecs(updatePeriod * 3600) <
                QDateTime.currentDateTime()) or \
           self.__lastUpdate.addSecs(updatePeriod * 3600) < \
                QDateTime.currentDateTime():
            self.updateNow()

    def updateNow(self):
        """
        Public method to update the subscription immediately.
        """
        if self.__downloading is not None:
            return

        if not self.location().isValid():
            return

        if self.location().scheme() == "file":
            self.__lastUpdate = QDateTime.currentDateTime()
            self.__loadRules()
            return

        import Helpviewer.HelpWindow
        from Helpviewer.Network.FollowRedirectReply import FollowRedirectReply
        self.__downloading = FollowRedirectReply(
            self.location(),
            Helpviewer.HelpWindow.HelpWindow.networkAccessManager())
        self.__downloading.finished.connect(self.__rulesDownloaded)

    def __rulesDownloaded(self):
        """
        Private slot to deal with the downloaded rules.
        """
        reply = self.sender()

        response = reply.readAll()
        reply.close()
        self.__downloading = None

        if reply.error() != QNetworkReply.NoError:
            if not self.__defaultSubscription:
                # don't show error if we try to load the default
                E5MessageBox.warning(
                    None, self.tr("Downloading subscription rules"),
                    self.tr("""<p>Subscription rules could not be"""
                            """ downloaded.</p><p>Error: {0}</p>""").format(
                                reply.errorString()))
            else:
                # reset after first download attempt
                self.__defaultSubscription = False
            return

        if response.isEmpty():
            E5MessageBox.warning(None,
                                 self.tr("Downloading subscription rules"),
                                 self.tr("""Got empty subscription rules."""))
            return

        fileName = self.rulesFileName()
        QFile.remove(fileName)
        f = QFile(fileName)
        if not f.open(QIODevice.ReadWrite):
            E5MessageBox.warning(
                None, self.tr("Downloading subscription rules"),
                self.tr("""Unable to open adblock file '{0}' for writing.""").
                file(fileName))
            return
        f.write(response)
        f.close()
        self.__lastUpdate = QDateTime.currentDateTime()
        if self.__validateCheckSum(fileName):
            self.__loadRules()
        else:
            QFile.remove(fileName)
        self.__downloading = None
        reply.deleteLater()

    def __validateCheckSum(self, fileName):
        """
        Private method to check the subscription file's checksum.
        
        @param fileName name of the file containing the subscription (string)
        @return flag indicating a valid file (boolean). A file is considered
            valid, if the checksum is OK or the file does not contain a
            checksum (i.e. cannot be checked).
        """
        try:
            f = open(fileName, "r", encoding="utf-8")
            data = f.read()
            f.close()
        except (IOError, OSError):
            return False

        match = re.search(self.__checksumRe, data)
        if match:
            expectedChecksum = match.group(1)
        else:
            # consider it as valid
            return True

        # normalize the data
        data = re.sub(r"\r", "", data)  # normalize eol
        data = re.sub(r"\n+", "\n", data)  # remove empty lines
        data = re.sub(self.__checksumRe, "", data)  # remove checksum line

        # calculate checksum
        md5 = hashlib.md5()
        md5.update(data.encode("utf-8"))
        calculatedChecksum = base64.b64encode(md5.digest()).decode()\
            .rstrip("=")
        if calculatedChecksum == expectedChecksum:
            return True
        else:
            res = E5MessageBox.yesNo(
                None, self.tr("Downloading subscription rules"),
                self.tr("""<p>AdBlock subscription <b>{0}</b> has a wrong"""
                        """ checksum.<br/>"""
                        """Found: {1}<br/>"""
                        """Calculated: {2}<br/>"""
                        """Use it anyway?</p>""").format(
                            self.__title, expectedChecksum,
                            calculatedChecksum))
            return res

    def saveRules(self):
        """
        Public method to save the subscription rules.
        """
        fileName = self.rulesFileName()
        if not fileName:
            return

        f = QFile(fileName)
        if not f.open(QIODevice.ReadWrite | QIODevice.Truncate):
            E5MessageBox.warning(
                None, self.tr("Saving subscription rules"),
                self.tr("""Unable to open adblock file '{0}' for writing.""").
                format(fileName))
            return

        textStream = QTextStream(f)
        if not self.__rules or not self.__rules[0].isHeader():
            textStream << "[Adblock Plus 1.1.1]\n"
        for rule in self.__rules:
            textStream << rule.filter() << "\n"

    def match(self, req, urlDomain, urlString):
        """
        Public method to check the subscription for a matching rule.
        
        @param req reference to the network request (QNetworkRequest)
        @param urlDomain domain of the URL (string)
        @param urlString URL (string)
        @return reference to the rule object or None (AdBlockRule)
        """
        for rule in self.__networkExceptionRules:
            if rule.networkMatch(req, urlDomain, urlString):
                return None

        for rule in self.__networkBlockRules:
            if rule.networkMatch(req, urlDomain, urlString):
                return rule

        return None

    def adBlockDisabledForUrl(self, url):
        """
        Public method to check, if AdBlock is disabled for the given URL.
        
        @param url URL to check (QUrl)
        @return flag indicating disabled state (boolean)
        """
        for rule in self.__documentRules:
            if rule.urlMatch(url):
                return True

        return False

    def elemHideDisabledForUrl(self, url):
        """
        Public method to check, if element hiding is disabled for the given
        URL.
        
        @param url URL to check (QUrl)
        @return flag indicating disabled state (boolean)
        """
        if self.adBlockDisabledForUrl(url):
            return True

        for rule in self.__elemhideRules:
            if rule.urlMatch(url):
                return True

        return False

    def elementHidingRules(self):
        """
        Public method to get the element hiding rules.
        
        @return element hiding rules (string)
        """
        return self.__elementHidingRules

    def elementHidingRulesForDomain(self, domain):
        """
        Public method to get the element hiding rules for the given domain.
        
        @param domain domain name (string)
        @return element hiding rules (string)
        """
        rules = ""

        for rule in self.__domainRestrictedCssRules:
            if rule.matchDomain(domain):
                rules += rule.cssSelector() + ","

        return rules

    def rule(self, offset):
        """
        Public method to get a specific rule.
        
        @param offset offset of the rule (integer)
        @return requested rule (AdBlockRule)
        """
        if offset >= len(self.__rules):
            return None

        return self.__rules[offset]

    def allRules(self):
        """
        Public method to get the list of rules.
        
        @return list of rules (list of AdBlockRule)
        """
        return self.__rules[:]

    def addRule(self, rule):
        """
        Public method to add a rule.
        
        @param rule reference to the rule to add (AdBlockRule)
        @return offset of the rule (integer)
        """
        self.__rules.append(rule)
        self.__populateCache()
        self.rulesChanged.emit()

        return len(self.__rules) - 1

    def removeRule(self, offset):
        """
        Public method to remove a rule given the offset.
        
        @param offset offset of the rule to remove (integer)
        """
        if offset < 0 or offset > len(self.__rules):
            return

        del self.__rules[offset]
        self.__populateCache()
        self.rulesChanged.emit()

    def replaceRule(self, rule, offset):
        """
        Public method to replace a rule given the offset.
        
        @param rule reference to the rule to set (AdBlockRule)
        @param offset offset of the rule to remove (integer)
        @return requested rule (AdBlockRule)
        """
        if offset >= len(self.__rules):
            return None

        self.__rules[offset] = rule
        self.__populateCache()
        self.rulesChanged.emit()

        return self.__rules[offset]

    def __populateCache(self):
        """
        Private method to populate the various rule caches.
        """
        self.__networkExceptionRules = []
        self.__networkBlockRules = []
        self.__domainRestrictedCssRules = []
        self.__elementHidingRules = ""
        self.__documentRules = []
        self.__elemhideRules = []

        for rule in self.__rules:
            if not rule.isEnabled():
                continue

            if rule.isCSSRule():
                if rule.isDomainRestricted():
                    self.__domainRestrictedCssRules.append(rule)
                else:
                    self.__elementHidingRules += rule.cssSelector() + ","
            elif rule.isDocument():
                self.__documentRules.append(rule)
            elif rule.isElementHiding():
                self.__elemhideRules.append(rule)
            elif rule.isException():
                self.__networkExceptionRules.append(rule)
            else:
                self.__networkBlockRules.append(rule)

    def canEditRules(self):
        """
        Public method to check, if rules can be edited.
        
        @return flag indicating rules may be edited (boolean)
        """
        return self.__custom

    def canBeRemoved(self):
        """
        Public method to check, if the subscription can be removed.
        
        @return flag indicating removal is allowed (boolean)
        """
        return not self.__custom and not self.__defaultSubscription

    def setRuleEnabled(self, offset, enabled):
        """
        Public method to enable a specific rule.
        
        @param offset offset of the rule (integer)
        @param enabled new enabled state (boolean)
        @return reference to the changed rule (AdBlockRule)
        """
        if offset >= len(self.__rules):
            return None

        rule = self.__rules[offset]
        rule.setEnabled(enabled)
        if rule.isCSSRule():
            import Helpviewer.HelpWindow
            self.__populateCache()
            Helpviewer.HelpWindow.HelpWindow.mainWindow()\
                .reloadUserStyleSheet()

        return rule
コード例 #12
0
class AdBlockSubscription(QObject):
    """
    Class implementing the AdBlock subscription.
    
    @signal changed() emitted after the subscription has changed
    @signal rulesChanged() emitted after the subscription's rules have changed
    @signal enabledChanged(bool) emitted after the enabled state was changed
    """
    changed = pyqtSignal()
    rulesChanged = pyqtSignal()
    enabledChanged = pyqtSignal(bool)
    
    def __init__(self, url, custom, parent=None, default=False):
        """
        Constructor
        
        @param url AdBlock URL for the subscription (QUrl)
        @param custom flag indicating a custom subscription (boolean)
        @param parent reference to the parent object (QObject)
        @param default flag indicating a default subscription (boolean)
        """
        super(AdBlockSubscription, self).__init__(parent)
        
        self.__custom = custom
        self.__url = url.toEncoded()
        self.__enabled = False
        self.__downloading = None
        self.__defaultSubscription = default
        
        self.__title = ""
        self.__location = QByteArray()
        self.__lastUpdate = QDateTime()
        self.__requiresLocation = ""
        self.__requiresTitle = ""
        
        self.__updatePeriod = 0     # update period in hours, 0 = use default
        self.__remoteModified = QDateTime()
        
        self.__rules = []   # list containing all AdBlock rules
        
        self.__networkExceptionRules = []
        self.__networkBlockRules = []
        self.__domainRestrictedCssRules = []
        self.__elementHidingRules = ""
        self.__documentRules = []
        self.__elemhideRules = []
        
        self.__checksumRe = re.compile(
            r"""^\s*!\s*checksum[\s\-:]+([\w\+\/=]+).*\n""",
            re.IGNORECASE | re.MULTILINE)
        self.__expiresRe = re.compile(
            r"""(?:expires:|expires after)\s*(\d+)\s*(hour|h)?""",
            re.IGNORECASE)
        self.__remoteModifiedRe = re.compile(
            r"""!\s*(?:Last modified|Updated):\s*(\d{1,2})\s*"""
            r"""(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*"""
            r"""(\d{2,4})\s*((\d{1,2}):(\d{2}))?""",
            re.IGNORECASE)
        
        self.__monthNameToNumber = {
            "Jan": 1,
            "Feb": 2,
            "Mar": 3,
            "Apr": 4,
            "May": 5,
            "Jun": 6,
            "Jul": 7,
            "Aug": 8,
            "Sep": 9,
            "Oct": 10,
            "Nov": 11,
            "Dec": 12
        }
        
        self.__parseUrl(url)
    
    def __parseUrl(self, url):
        """
        Private method to parse the AdBlock URL for the subscription.
        
        @param url AdBlock URL for the subscription (QUrl)
        """
        if url.scheme() != "abp":
            return
        
        if url.path() != "subscribe":
            return
        
        if qVersion() >= "5.0.0":
            from PyQt5.QtCore import QUrlQuery
            urlQuery = QUrlQuery(url)
            self.__title = QUrl.fromPercentEncoding(
                QByteArray(urlQuery.queryItemValue("title").encode()))
            self.__enabled = urlQuery.queryItemValue("enabled") != "false"
            self.__location = QByteArray(QUrl.fromPercentEncoding(
                QByteArray(urlQuery.queryItemValue("location").encode()))
                .encode("utf-8"))
            
            # Check for required subscription
            self.__requiresLocation = QUrl.fromPercentEncoding(
                QByteArray(urlQuery.queryItemValue(
                    "requiresLocation").encode()))
            self.__requiresTitle = QUrl.fromPercentEncoding(
                QByteArray(urlQuery.queryItemValue("requiresTitle").encode()))
            if self.__requiresLocation and self.__requiresTitle:
                import Helpviewer.HelpWindow
                Helpviewer.HelpWindow.HelpWindow.adBlockManager()\
                    .loadRequiredSubscription(self.__requiresLocation,
                                              self.__requiresTitle)
            
            lastUpdateString = urlQuery.queryItemValue("lastUpdate")
            self.__lastUpdate = QDateTime.fromString(lastUpdateString,
                                                     Qt.ISODate)
        else:
            self.__title = \
                QUrl.fromPercentEncoding(url.encodedQueryItemValue(b"title"))
            self.__enabled = QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"enabled")) != "false"
            self.__location = QByteArray(QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"location")).encode("utf-8"))
            
            # Check for required subscription
            self.__requiresLocation = QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"requiresLocation"))
            self.__requiresTitle = QUrl.fromPercentEncoding(
                url.encodedQueryItemValue(b"requiresTitle"))
            if self.__requiresLocation and self.__requiresTitle:
                import Helpviewer.HelpWindow
                Helpviewer.HelpWindow.HelpWindow.adBlockManager()\
                    .loadRequiredSubscription(self.__requiresLocation,
                                              self.__requiresTitle)
            
            lastUpdateByteArray = url.encodedQueryItemValue(b"lastUpdate")
            lastUpdateString = QUrl.fromPercentEncoding(lastUpdateByteArray)
            self.__lastUpdate = QDateTime.fromString(lastUpdateString,
                                                     Qt.ISODate)
        
        self.__loadRules()
    
    def url(self):
        """
        Public method to generate the URL for this subscription.
        
        @return AdBlock URL for the subscription (QUrl)
        """
        url = QUrl()
        url.setScheme("abp")
        url.setPath("subscribe")
        
        queryItems = []
        queryItems.append(("location", bytes(self.__location).decode()))
        queryItems.append(("title", self.__title))
        if self.__requiresLocation and self.__requiresTitle:
            queryItems.append(("requiresLocation", self.__requiresLocation))
            queryItems.append(("requiresTitle", self.__requiresTitle))
        if not self.__enabled:
            queryItems.append(("enabled", "false"))
        if self.__lastUpdate.isValid():
            queryItems.append(("lastUpdate",
                               self.__lastUpdate.toString(Qt.ISODate)))
        if qVersion() >= "5.0.0":
            from PyQt5.QtCore import QUrlQuery
            query = QUrlQuery()
            query.setQueryItems(queryItems)
            url.setQuery(query)
        else:
            url.setQueryItems(queryItems)
        return url
    
    def isEnabled(self):
        """
        Public method to check, if the subscription is enabled.
        
        @return flag indicating the enabled status (boolean)
        """
        return self.__enabled
    
    def setEnabled(self, enabled):
        """
        Public method to set the enabled status.
        
        @param enabled flag indicating the enabled status (boolean)
        """
        if self.__enabled == enabled:
            return
        
        self.__enabled = enabled
        self.enabledChanged.emit(enabled)
    
    def title(self):
        """
        Public method to get the subscription title.
        
        @return subscription title (string)
        """
        return self.__title
    
    def setTitle(self, title):
        """
        Public method to set the subscription title.
        
        @param title subscription title (string)
        """
        if self.__title == title:
            return
        
        self.__title = title
        self.changed.emit()
    
    def location(self):
        """
        Public method to get the subscription location.
        
        @return URL of the subscription location (QUrl)
        """
        return QUrl.fromEncoded(self.__location)
    
    def setLocation(self, url):
        """
        Public method to set the subscription location.
        
        @param url URL of the subscription location (QUrl)
        """
        if url == self.location():
            return
        
        self.__location = url.toEncoded()
        self.__lastUpdate = QDateTime()
        self.changed.emit()
    
    def requiresLocation(self):
        """
        Public method to get the location of a required subscription.
        
        @return location of a required subscription (string)
        """
        return self.__requiresLocation
    
    def lastUpdate(self):
        """
        Public method to get the date and time of the last update.
        
        @return date and time of the last update (QDateTime)
        """
        return self.__lastUpdate
    
    def rulesFileName(self):
        """
        Public method to get the name of the rules file.
        
        @return name of the rules file (string)
        """
        if self.location().scheme() == "file":
            return self.location().toLocalFile()
        
        if self.__location.isEmpty():
            return ""
        
        sha1 = bytes(QCryptographicHash.hash(
            self.__location, QCryptographicHash.Sha1).toHex()).decode()
        dataDir = os.path.join(
            Utilities.getConfigDir(), "browser", "subscriptions")
        if not os.path.exists(dataDir):
            os.makedirs(dataDir)
        fileName = os.path.join(
            dataDir, "adblock_subscription_{0}".format(sha1))
        return fileName
    
    def __loadRules(self):
        """
        Private method to load the rules of the subscription.
        """
        fileName = self.rulesFileName()
        f = QFile(fileName)
        if f.exists():
            if not f.open(QIODevice.ReadOnly):
                E5MessageBox.warning(
                    None,
                    self.tr("Load subscription rules"),
                    self.tr(
                        """Unable to open adblock file '{0}' for reading.""")
                    .format(fileName))
            else:
                textStream = QTextStream(f)
                header = textStream.readLine(1024)
                if not header.startswith("[Adblock"):
                    E5MessageBox.warning(
                        None,
                        self.tr("Load subscription rules"),
                        self.tr("""AdBlock file '{0}' does not start"""
                                """ with [Adblock.""")
                        .format(fileName))
                    f.close()
                    f.remove()
                    self.__lastUpdate = QDateTime()
                else:
                    from .AdBlockRule import AdBlockRule
                    
                    self.__updatePeriod = 0
                    self.__remoteModified = QDateTime()
                    self.__rules = []
                    self.__rules.append(AdBlockRule(header, self))
                    while not textStream.atEnd():
                        line = textStream.readLine()
                        self.__rules.append(AdBlockRule(line, self))
                        expires = self.__expiresRe.search(line)
                        if expires:
                            period, kind = expires.groups()
                            if kind:
                                # hours
                                self.__updatePeriod = int(period)
                            else:
                                # days
                                self.__updatePeriod = int(period) * 24
                        remoteModified = self.__remoteModifiedRe.search(line)
                        if remoteModified:
                            day, month, year, time, hour, minute = \
                                remoteModified.groups()
                            self.__remoteModified.setDate(
                                QDate(int(year),
                                      self.__monthNameToNumber[month],
                                      int(day))
                            )
                            if time:
                                self.__remoteModified.setTime(
                                    QTime(int(hour), int(minute)))
                    self.__populateCache()
                    self.changed.emit()
        elif not fileName.endswith("_custom"):
            self.__lastUpdate = QDateTime()
        
        self.checkForUpdate()
    
    def checkForUpdate(self):
        """
        Public method to check for an update.
        """
        if self.__updatePeriod:
            updatePeriod = self.__updatePeriod
        else:
            updatePeriod = Preferences.getHelp("AdBlockUpdatePeriod") * 24
        if not self.__lastUpdate.isValid() or \
           (self.__remoteModified.isValid() and
            self.__remoteModified.addSecs(updatePeriod * 3600) <
                QDateTime.currentDateTime()) or \
           self.__lastUpdate.addSecs(updatePeriod * 3600) < \
                QDateTime.currentDateTime():
            self.updateNow()
    
    def updateNow(self):
        """
        Public method to update the subscription immediately.
        """
        if self.__downloading is not None:
            return
        
        if not self.location().isValid():
            return
        
        if self.location().scheme() == "file":
            self.__lastUpdate = QDateTime.currentDateTime()
            self.__loadRules()
            return
        
        import Helpviewer.HelpWindow
        from Helpviewer.Network.FollowRedirectReply import FollowRedirectReply
        self.__downloading = FollowRedirectReply(
            self.location(),
            Helpviewer.HelpWindow.HelpWindow.networkAccessManager())
        self.__downloading.finished.connect(self.__rulesDownloaded)
    
    def __rulesDownloaded(self):
        """
        Private slot to deal with the downloaded rules.
        """
        reply = self.sender()
        
        response = reply.readAll()
        reply.close()
        self.__downloading = None
        
        if reply.error() != QNetworkReply.NoError:
            if not self.__defaultSubscription:
                # don't show error if we try to load the default
                E5MessageBox.warning(
                    None,
                    self.tr("Downloading subscription rules"),
                    self.tr(
                        """<p>Subscription rules could not be"""
                        """ downloaded.</p><p>Error: {0}</p>""")
                    .format(reply.errorString()))
            else:
                # reset after first download attempt
                self.__defaultSubscription = False
            return
        
        if response.isEmpty():
            E5MessageBox.warning(
                None,
                self.tr("Downloading subscription rules"),
                self.tr("""Got empty subscription rules."""))
            return
        
        fileName = self.rulesFileName()
        QFile.remove(fileName)
        f = QFile(fileName)
        if not f.open(QIODevice.ReadWrite):
            E5MessageBox.warning(
                None,
                self.tr("Downloading subscription rules"),
                self.tr(
                    """Unable to open adblock file '{0}' for writing.""")
                .file(fileName))
            return
        f.write(response)
        f.close()
        self.__lastUpdate = QDateTime.currentDateTime()
        if self.__validateCheckSum(fileName):
            self.__loadRules()
        else:
            QFile.remove(fileName)
        self.__downloading = None
        reply.deleteLater()
    
    def __validateCheckSum(self, fileName):
        """
        Private method to check the subscription file's checksum.
        
        @param fileName name of the file containing the subscription (string)
        @return flag indicating a valid file (boolean). A file is considered
            valid, if the checksum is OK or the file does not contain a
            checksum (i.e. cannot be checked).
        """
        try:
            f = open(fileName, "r", encoding="utf-8")
            data = f.read()
            f.close()
        except (IOError, OSError):
            return False
        
        match = re.search(self.__checksumRe, data)
        if match:
            expectedChecksum = match.group(1)
        else:
            # consider it as valid
            return True
        
        # normalize the data
        data = re.sub(r"\r", "", data)              # normalize eol
        data = re.sub(r"\n+", "\n", data)           # remove empty lines
        data = re.sub(self.__checksumRe, "", data)  # remove checksum line
        
        # calculate checksum
        md5 = hashlib.md5()
        md5.update(data.encode("utf-8"))
        calculatedChecksum = base64.b64encode(md5.digest()).decode()\
            .rstrip("=")
        if calculatedChecksum == expectedChecksum:
            return True
        else:
            res = E5MessageBox.yesNo(
                None,
                self.tr("Downloading subscription rules"),
                self.tr(
                    """<p>AdBlock subscription <b>{0}</b> has a wrong"""
                    """ checksum.<br/>"""
                    """Found: {1}<br/>"""
                    """Calculated: {2}<br/>"""
                    """Use it anyway?</p>""")
                .format(self.__title, expectedChecksum,
                        calculatedChecksum))
            return res
    
    def saveRules(self):
        """
        Public method to save the subscription rules.
        """
        fileName = self.rulesFileName()
        if not fileName:
            return
        
        f = QFile(fileName)
        if not f.open(QIODevice.ReadWrite | QIODevice.Truncate):
            E5MessageBox.warning(
                None,
                self.tr("Saving subscription rules"),
                self.tr(
                    """Unable to open adblock file '{0}' for writing.""")
                .format(fileName))
            return
        
        textStream = QTextStream(f)
        if not self.__rules or not self.__rules[0].isHeader():
            textStream << "[Adblock Plus 1.1.1]\n"
        for rule in self.__rules:
            textStream << rule.filter() << "\n"
    
    def match(self, req, urlDomain, urlString):
        """
        Public method to check the subscription for a matching rule.
        
        @param req reference to the network request (QNetworkRequest)
        @param urlDomain domain of the URL (string)
        @param urlString URL (string)
        @return reference to the rule object or None (AdBlockRule)
        """
        for rule in self.__networkExceptionRules:
            if rule.networkMatch(req, urlDomain, urlString):
                return None
        
        for rule in self.__networkBlockRules:
            if rule.networkMatch(req, urlDomain, urlString):
                return rule
        
        return None
    
    def adBlockDisabledForUrl(self, url):
        """
        Public method to check, if AdBlock is disabled for the given URL.
        
        @param url URL to check (QUrl)
        @return flag indicating disabled state (boolean)
        """
        for rule in self.__documentRules:
            if rule.urlMatch(url):
                return True
        
        return False
    
    def elemHideDisabledForUrl(self, url):
        """
        Public method to check, if element hiding is disabled for the given
        URL.
        
        @param url URL to check (QUrl)
        @return flag indicating disabled state (boolean)
        """
        if self.adBlockDisabledForUrl(url):
            return True
        
        for rule in self.__elemhideRules:
            if rule.urlMatch(url):
                return True
        
        return False
    
    def elementHidingRules(self):
        """
        Public method to get the element hiding rules.
        
        @return element hiding rules (string)
        """
        return self.__elementHidingRules
    
    def elementHidingRulesForDomain(self, domain):
        """
        Public method to get the element hiding rules for the given domain.
        
        @param domain domain name (string)
        @return element hiding rules (string)
        """
        rules = ""
        
        for rule in self.__domainRestrictedCssRules:
            if rule.matchDomain(domain):
                rules += rule.cssSelector() + ","
        
        return rules
    
    def rule(self, offset):
        """
        Public method to get a specific rule.
        
        @param offset offset of the rule (integer)
        @return requested rule (AdBlockRule)
        """
        if offset >= len(self.__rules):
            return None
        
        return self.__rules[offset]
    
    def allRules(self):
        """
        Public method to get the list of rules.
        
        @return list of rules (list of AdBlockRule)
        """
        return self.__rules[:]
    
    def addRule(self, rule):
        """
        Public method to add a rule.
        
        @param rule reference to the rule to add (AdBlockRule)
        @return offset of the rule (integer)
        """
        self.__rules.append(rule)
        self.__populateCache()
        self.rulesChanged.emit()
        
        return len(self.__rules) - 1
    
    def removeRule(self, offset):
        """
        Public method to remove a rule given the offset.
        
        @param offset offset of the rule to remove (integer)
        """
        if offset < 0 or offset > len(self.__rules):
            return
        
        del self.__rules[offset]
        self.__populateCache()
        self.rulesChanged.emit()
    
    def replaceRule(self, rule, offset):
        """
        Public method to replace a rule given the offset.
        
        @param rule reference to the rule to set (AdBlockRule)
        @param offset offset of the rule to remove (integer)
        @return requested rule (AdBlockRule)
        """
        if offset >= len(self.__rules):
            return None
        
        self.__rules[offset] = rule
        self.__populateCache()
        self.rulesChanged.emit()
        
        return self.__rules[offset]
    
    def __populateCache(self):
        """
        Private method to populate the various rule caches.
        """
        self.__networkExceptionRules = []
        self.__networkBlockRules = []
        self.__domainRestrictedCssRules = []
        self.__elementHidingRules = ""
        self.__documentRules = []
        self.__elemhideRules = []
        
        for rule in self.__rules:
            if not rule.isEnabled():
                continue
            
            if rule.isCSSRule():
                if rule.isDomainRestricted():
                    self.__domainRestrictedCssRules.append(rule)
                else:
                    self.__elementHidingRules += rule.cssSelector() + ","
            elif rule.isDocument():
                self.__documentRules.append(rule)
            elif rule.isElementHiding():
                self.__elemhideRules.append(rule)
            elif rule.isException():
                self.__networkExceptionRules.append(rule)
            else:
                self.__networkBlockRules.append(rule)
    
    def canEditRules(self):
        """
        Public method to check, if rules can be edited.
        
        @return flag indicating rules may be edited (boolean)
        """
        return self.__custom
    
    def canBeRemoved(self):
        """
        Public method to check, if the subscription can be removed.
        
        @return flag indicating removal is allowed (boolean)
        """
        return not self.__custom and not self.__defaultSubscription
    
    def setRuleEnabled(self, offset, enabled):
        """
        Public method to enable a specific rule.
        
        @param offset offset of the rule (integer)
        @param enabled new enabled state (boolean)
        @return reference to the changed rule (AdBlockRule)
        """
        if offset >= len(self.__rules):
            return None
        
        rule = self.__rules[offset]
        rule.setEnabled(enabled)
        if rule.isCSSRule():
            import Helpviewer.HelpWindow
            self.__populateCache()
            Helpviewer.HelpWindow.HelpWindow.mainWindow()\
                .reloadUserStyleSheet()
        
        return rule
コード例 #13
0
class MainForm(QDialog):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)

        self.filename = ""
        self.copiedItem = QByteArray()
        self.pasteOffset = 5
        self.prevPoint = QPoint()
        self.addOffset = 5
        self.borders = []

        self.printer = QPrinter(QPrinter.HighResolution)
        # enum QPrinter::PrinterMode
        # HighRes是一个高分辨率模式,是PrinterMode组成
        self.printer.setPageSize(QPrinter.A4)
        self.view = GraphicsView()
        self.scene = QGraphicsScene(self)
        self.scene.setSceneRect(0, 0, PageSize[0], PageSize[1])
        self.addBorders()
        self.view.setScene(self.scene)
        # 不用写 view.show?
        self.wrapped = []  # Needed to keep wrappers alive
        buttonLayout = QVBoxLayout()
        for text, slot in (("Add &Text", self.addText), ("Add &Box",
                                                         self.addBox),
                           ("Add Pi&xmap", self.addPixmap), ("&Align", None),
                           ("&Copy", self.copy), ("C&ut", self.cut),
                           ("&Paste", self.paste), ("&Delete...", self.delete),
                           ("&Rotate", self.rotate),
                           ("Pri&nt...", self.print_), ("&Open...", self.open),
                           ("&Save", self.save), ("&Quit", self.accept)):
            button = QPushButton(text)
            if not MAC:
                button.setFocusPolicy(Qt.NoFocus)
            if slot is not None:
                button.clicked.connect(slot)
            if text == "&Align":
                menu = QMenu(self)
                for text, arg in (("Align &Left", Qt.AlignLeft),
                                  ("Align &Right",
                                   Qt.AlignRight), ("Align &Top", Qt.AlignTop),
                                  ("Align &Bottom", Qt.AlignBottom)):
                    wrapper = functools.partial(self.setAlignment, arg)
                    # ?????
                    self.wrapped.append(wrapper)
                    menu.addAction(text, wrapper)
                button.setMenu(menu)
            if text == "Pri&nt...":
                buttonLayout.addStretch(5)
            if text == "&Quit":
                buttonLayout.addStretch(1)
            buttonLayout.addWidget(button)
        buttonLayout.addStretch()

        layout = QHBoxLayout()
        layout.addWidget(self.view, 1)
        # QBoxLayout::addWidget(QWidget *widget, int stretch = 0, Qt::Alignment alignment = Qt::Alignment())
        layout.addLayout(buttonLayout)
        self.setLayout(layout)

        fm = QFontMetrics(self.font())
        self.resize(self.scene.width() + fm.width(" Delete... ") + 50,
                    self.scene.height() + 50)
        self.setWindowTitle("Page Designer 页面设计器")

    def addBorders(self):
        '''添加出血框和打印边界框,对scene进行操作同时添加到self.borders这一列表中'''
        self.borders = []
        rect = QRectF(0, 0, PageSize[0], PageSize[1])
        self.borders.append(self.scene.addRect(
            rect,
            Qt.black))  # addRect px,py,x,y,QPen,QBrush or QRectF,QPen,QBrush
        # scene.addRect(): Return QGraphicsRectItem;Inherits: QGraphicsItem

        margin = 5.25 * PointSize
        self.borders.append(
            self.scene.addRect(rect.adjusted(margin, margin, -margin, -margin),
                               Qt.red))

    def removeBorders(self):
        '''从列表删除边框,从scene删除边框'''
        while self.borders:
            item = self.borders.pop()
            self.scene.removeItem(
                item
            )  #Removes the item item and all its children from the scene. 接受参数为QGraphicsItem
            del item

    def reject(self):
        self.accept()

    def accept(self):
        self.offerSave()
        QDialog.accept(
            self
        )  # 完成提示保存之后传递给QDialog的accept命令,之前几章好像讲过为什么要直接调用QDialog这个父类,这里的MWindow是QDialog

    def offerSave(self):
        '''根据Dirty判断是否更改,如果更改则弹出保存对话框,调用save()函数进行保存。'''
        if (Dirty and QMessageBox.question(
                self, "Page Designer - Unsaved Changes",
                "Save unsaved changes?", QMessageBox.Yes | QMessageBox.No)
                == QMessageBox.Yes):
            self.save()

    def position(self):
        point = self.mapFromGlobal(QCursor.pos(
        ))  # mFG接受一个QPoint参数,包含两个元素的元组 此函数转换QPoint到map,返回依旧是QPoint
        # Translates the global screen coordinate pos to widget coordinates.
        if not self.view.geometry().contains(
                point):  #??????????????????????????????????????????????
            coord = random.randint(36, 144)
            point = QPoint(coord, coord)
        else:
            if point == self.prevPoint:
                point += QPoint(self.addOffset, self.addOffset)
                self.addOffset += 5
            else:
                self.addOffset = 5
                self.prevPoint = point
        return self.view.mapToScene(
            point)  # 将Widght的点左边转换成为Scene坐标,调用对象是QGView

    def addText(self):
        dialog = TextItemDlg(position=self.position(),
                             scene=self.scene,
                             parent=self)
        dialog.exec_()

    def addBox(self):
        BoxItem(self.position(), self.scene)

    def addPixmap(self):
        path = (QFileInfo(self.filename).path() if self.filename else "."
                )  # 获取filename定义的正确地址,或者返回此程序根目录
        fname, filetype = QFileDialog.getOpenFileName(
            self, "Page Designer - Add Pixmap", path,
            "Pixmap Files (*.bmp *.jpg *.png *.xpm)")
        if not fname:
            return
        self.createPixmapItem(
            QPixmap(fname),
            self.position())  # 插入时候要将地址传递给QPixmap生成对象,并且还需要位置参数

    def createPixmapItem(self, pixmap, position,
                         matrix=QTransform()):  # 传递参数为:文件、位置和变换
        item = GraphicsPixmapItem(pixmap)  # 第一步,将QPixmap转换成为GPItem
        item.setFlags(QGraphicsItem.ItemIsSelectable
                      | QGraphicsItem.ItemIsMovable)  # 设置一些属性
        item.setPos(position)  # 设置位置
        item.setTransform(matrix)  # 将变换参数应用到GPItem之中
        self.scene.clearSelection()  # 选择清空
        self.scene.addItem(item)  # 添加项目
        item.setSelected(True)  # 并且选中
        global Dirty
        Dirty = True  # 全局变量Dirty设置为True
        return item  # 为什么需要返回这个???

    def selectedItem(
            self):  # 默认的scene选择的是一个列表,如果只有一个则返回index=0的item,如果多选则不返回任何一个
        items = self.scene.selectedItems()
        if len(items) == 1:
            return items[0]
        return None

    def copy(self):
        item = self.selectedItem()
        if item is None:
            return
        self.copiedItem.clear()
        self.pasteOffset = 5
        stream = QDataStream(self.copiedItem, QIODevice.WriteOnly)
        self.writeItemToStream(stream, item)  # 写入到流

    def cut(self):
        item = self.selectedItem()
        if item is None:
            return
        self.copy()
        self.scene.removeItem(item)
        del item

    def paste(self):
        if self.copiedItem.isEmpty():
            return
        stream = QDataStream(self.copiedItem, QIODevice.ReadOnly)
        self.readItemFromStream(
            stream, self.pasteOffset)  # 从数据流中读入信息,并且输出到self.pasteOffset中
        self.pasteOffset += 5

    def setAlignment(self, alignment):
        # Items are returned in arbitrary order
        items = self.scene.selectedItems()
        if len(items) <= 1:
            return
        # Gather coordinate data
        leftXs, rightXs, topYs, bottomYs = [], [], [], []
        for item in items:
            rect = item.sceneBoundingRect()
            # Returns the bounding rect of this item in scene coordinates : Return QRectF
            leftXs.append(rect.x())
            rightXs.append(rect.x() + rect.width())
            topYs.append(rect.y())
            bottomYs.append(rect.y() + rect.height())
        # Perform alignment
        if alignment == Qt.AlignLeft:
            xAlignment = min(leftXs)
            for i, item in enumerate(items):
                item.moveBy(xAlignment - leftXs[i], 0)
                # void QGraphicsItem::moveBy(qreal dx, qreal dy)
                # Moves the item by dx points horizontally, and dy point vertically.
        elif alignment == Qt.AlignRight:
            xAlignment = max(rightXs)
            for i, item in enumerate(items):
                item.moveBy(xAlignment - rightXs[i], 0)
        elif alignment == Qt.AlignTop:
            yAlignment = min(topYs)
            for i, item in enumerate(items):
                item.moveBy(0, yAlignment - topYs[i])
        elif alignment == Qt.AlignBottom:
            yAlignment = max(bottomYs)
            for i, item in enumerate(items):
                item.moveBy(0, yAlignment - bottomYs[i])
        global Dirty
        Dirty = True

    def rotate(self):
        for item in self.scene.selectedItems():
            item.setRotation(item.rotation() + 30)

    def delete(self):  # 从基本scene属性中选取选择的多个,弹出对话框,如果允许,则迭代进行删除,并且设置Dirty为True
        items = self.scene.selectedItems()
        if (len(items) and QMessageBox.question(
                self, "Page Designer - Delete", "Delete {0} item{1}?".format(
                    len(items), "s" if len(items) != 1 else ""),
                QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes):
            while items:
                item = items.pop()
                self.scene.removeItem(item)
                del item
            global Dirty
            Dirty = True

    def print_(self):
        # dialog = QPrintDialog(self.printer) # 在此已经设置好了self.printer 也就是QPrinter对象,QPDlg直接传递回了Printer对象,之后重新由
        # # printer对象声称心的QPrinter就可以继续使用在这句话中设置好的参数了。
        # if dialog.exec_():
        painter = QPainter(self.printer)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.TextAntialiasing)
        self.scene.clearSelection()
        self.removeBorders()
        self.scene.render(painter)
        # [void] Renders the source rect from scene into target, using painter. This function is useful for capturing the contents
        # of the scene onto a paint device, such as a QImage (e.g., to take a screenshot), or for printing with QPrinter. For example:
        self.addBorders()

    def open(self):
        self.offerSave()
        path = (QFileInfo(self.filename).path() if self.filename else ".")
        fname, filetype = QFileDialog.getOpenFileName(
            self, "Page Designer - Open", path,
            "cmPage Designer Files (*.cmpd *.pgd *.cmd)")
        if not fname:
            return
        self.filename = fname
        fh = None
        try:
            fh = QFile(self.filename)
            if not fh.open(QIODevice.ReadOnly):
                raise IOError(str(fh.errorString()))
            items = self.scene.items()  # 返回所有的QGitem List形式
            while items:
                item = items.pop()  # 从列表中删除一个,从scene中删除一个,迭代到全部删除
                self.scene.removeItem(item)
                del item
            self.addBorders()
            stream = QDataStream(fh)
            stream.setVersion(QDataStream.Qt_5_7)
            magic = stream.readInt32()
            if magic != MagicNumber:
                raise IOError("not a valid .cmpd file")
            fileVersion = stream.readInt16()
            if fileVersion != FileVersion:
                raise IOError("unrecognised .cmpd file version")
            while not fh.atEnd():
                self.readItemFromStream(stream)
        except IOError as e:
            QMessageBox.warning(
                self, "Page Designer -- Open Error",
                "Failed to open {0}: {1}".format(self.filename, e))
        finally:
            if fh is not None:
                fh.close()
        global Dirty
        Dirty = False

    def save(self):
        if not self.filename:
            path = "."
            fname, filetype = QFileDialog.getSaveFileName(
                self, "Page Designer - Save As", path,
                "cmPage Designer Files (*.cmpd *.pgd *.cmd)")
            if not fname:
                return
            self.filename = fname
        fh = None
        try:
            fh = QFile(self.filename)
            if not fh.open(QIODevice.WriteOnly):
                raise IOError(str(fh.errorString()))
            self.scene.clearSelection()
            stream = QDataStream(fh)
            stream.setVersion(QDataStream.Qt_5_7)
            stream.writeInt32(MagicNumber)
            stream.writeInt16(FileVersion)
            for item in self.scene.items():
                self.writeItemToStream(stream, item)
        except IOError as e:
            QMessageBox.warning(
                self, "Page Designer -- Save Error",
                "Failed to save {0}: {1}".format(self.filename, e))
        finally:
            if fh is not None:
                fh.close()
        global Dirty
        Dirty = False

    def readItemFromStream(self, stream, offset=0):
        type = ""
        position = QPointF()
        matrix = QTransform()
        rotateangle = 0  #add by yangrongdong
        type = stream.readQString()
        stream >> position >> matrix
        if offset:
            position += QPointF(offset, offset)
        if type == "Text":
            text = ""
            font = QFont()
            text = stream.readQString()
            stream >> font
            rotateangle = stream.readFloat()
            tx = TextItem(text, position, self.scene, font, matrix)
            tx.setRotation(rotateangle)
        elif type == "Box":
            rect = QRectF()
            stream >> rect
            style = Qt.PenStyle(stream.readInt16())
            rotateangle = stream.readFloat()
            bx = BoxItem(position, self.scene, style, rect, matrix)
            bx.setRotation(rotateangle)
        elif type == "Pixmap":
            pixmap = QPixmap()
            stream >> pixmap
            rotateangle = stream.readFloat()
            px = self.createPixmapItem(pixmap, position, matrix)
            px.setRotation(rotateangle)

    def writeItemToStream(self, stream, item):
        if isinstance(item, TextItem):
            stream.writeQString("Text")
            stream << item.pos() << item.transform()
            stream.writeQString(item.toPlainText())
            stream << item.font()
            stream.writeFloat(item.rotation())  #add by yangrongdong
        elif isinstance(item, GraphicsPixmapItem):
            stream.writeQString("Pixmap")
            stream << item.pos() << item.transform() << item.pixmap()
            stream.writeFloat(item.rotation())  #add by yangrongdong
        elif isinstance(item, BoxItem):
            stream.writeQString("Box")
            stream << item.pos() << item.transform() << item.rect
            stream.writeInt16(item.style)
            stream.writeFloat(item.rotation())  #add by yangrongdong