Пример #1
0
    def sendMessage(self, msg, args, id = None):
        """Sends a message.

        msg -- The message type.
        args -- List of message arguments.
        id -- An optional socket descriptor. If specified, then the message will only be sent if this connection's socket descriptor matches id."""
        if id:
            if self.socketDescriptor() != id:
                return
        msg = Message.fromInt(msg)
        if not msg.validateArgs(args):
            log.warning("Message %d and args %s have invalid types. Message not sent.", msg, args)
            return
        if self.client:
            log.debug("Sending %s %s", msg, args)
        elif self.player and self.player.name:
            log.debug("Sending %s %s to %s", msg, args, self.player)
        else:
            log.debug("Sending %s %s to %s", msg, args, self.peerAddress().toString())
        data = QByteArray()
        stream = QDataStream(data, self.WriteOnly)
        stream.writeInt32(int(msg))
        for arg in args:
            if type(arg) == str:
                stream.writeInt32(len(arg))
                stream.writeRawData(arg)
            elif type(arg) == int:
                stream.writeInt32(arg)
            elif type(arg) == long:
                stream.writeInt64(arg)
        self.write(data)
        self.messageSent.emit(msg, args)
Пример #2
0
    def send(self, command, *args):
        self._logger.info("GC<<: {}:{}".format(command, args))
        ds = QDataStream(self._socket)
        ds.setByteOrder(QDataStream.LittleEndian)

        # Header
        ds.writeUInt32(len(command))
        ds.writeRawData(command.encode())

        # Chunks
        ds.writeUInt32(len(args))

        for chunk in args:
            ds.writeRawData(self._packLuaVal(chunk))
Пример #3
0
class MainWindow(QMainWindow):
    STD_RIP_FILE = 0x1111FFFF
    NEW_RIP_FILE = 0x2222FFFF
    CALDERA_RIP_FILE = 0xFFFF1111

    ver = '1.3.10'

    colors = {'Black':'K', 'Cyan':'C', 'Magenta':'M', 'Yellow':'Y', 'Light Cyan':'c', 'Light Magenta':'m', 'White': 'W',
              'Varnish':'V', 'Orange':'O', 'Green':'G', 'Gold':'D', 'Silver':'S', 'Medium Black':'k', 'Light Gray':'g',
              'Red':'R', 'Blue':'B', 'Violet':'v', 'Spot Color 1':'1', 'Spot Color 2':'2', 'Spot Color 3':'3',
              'Spot Color 4':'4', 'Spot Color 5':'5', 'Spot Color 6':'6', 'Spot Color 7':'7', 'Spot Color 8':'8', 'Spot Color 9':'9'}

    listColors = ['Black', 'Cyan', 'Magenta', 'Yellow', 'Light Cyan', 'Light Magenta', 'White', 'Varnish', 'Orange',
                  'Red', 'Green', 'Blue', 'Violet', 'Gold', 'Silver', 'Medium Black', 'Light Gray', 'Spot Color 1',
                  'Spot Color 2', 'Spot Color 3', 'Spot Color 4', 'Spot Color 5', 'Spot Color 6', 'Spot Color 7', 'Spot Color 8', 'Spot Color 9']

    levelGS = {'1 bit per drop (Binary)':1, '2 bit per drop':2, '4 bit per drop (not used)':4 }

    acceptedFormats = ['tif', 'bmp']

    listGSLevel = ['1 bit per drop (Binary)', '2 bit per drop']#,'4 bit per drop (not used)']

    black = qRgb(0, 0, 0)

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

#        self.stackLayout = QStackedLayout()

        self.settings = QSettings('settings.ini', QSettings.IniFormat)

        self.vLayout = QVBoxLayout()
        self.toolBar = QToolBar('mainToolBar')
        self.addToolBar(Qt.RightToolBarArea, self.toolBar)
        self.centralWidget = QSplitter(Qt.Vertical)
        self.setCentralWidget(self.centralWidget)

        self.status = self.statusBar()

        self.colorWidgets = {}
        self.levelWidgets = {}

        self.cmbGS = QComboBox()
        self.cmbGS.addItems(self.listGSLevel)
        self.connect(self.cmbGS, SIGNAL("currentIndexChanged(int)"), self.fillCmbGSLevel) 
        self.gsLevel = self.levelGS[self.cmbGS.currentText()]

        self.xdpiCmbBox = QComboBox()
        self.xdpiCmbBox.addItems(['720', '600', '360', '300', '180', '150'])

        self.ydpiCmbBox = QComboBox()
        self.ydpiCmbBox.addItems(['360', '300', '180', '150', '90', '75'])

        self.table = QTableWidget(0,4)
        self.connect(self.table, SIGNAL("cellClicked(int, int)"), self.tableCellActivated)
        self.table.setHorizontalHeaderLabels(['Filename', 'Color', 'GS Level', 'Size'])
        self._table = TableWidget(self.table, self.cmbGS, self.xdpiCmbBox, self.ydpiCmbBox)
        self.connect(self._table, SIGNAL("insertRowAfter(int)"), self.addFile)
        self.connect(self._table, SIGNAL("removeRowAt(int)"), self.removeFile)
#        self._table.setAcceptDrops(True)
        self.table.setAcceptDrops(True)

        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)

        self.scale = False
        self.chkBoxScale = QCheckBox("See the Whole")
        self.connect(self.chkBoxScale, SIGNAL("toggled(bool)"), self.setScale)

        self.centralWidget.addWidget(self._table)

        vbox = QVBoxLayout()
        vbox.addWidget(self.chkBoxScale)
        vbox.addWidget(self.view)
        widget = QWidget()
        widget.setLayout(vbox)
        self.centralWidget.addWidget(widget)
#        self.centralWidget.addWidget(self.statusBar)

        savePrnAction = self.createAction('Create .prn',self.savePrnFile, QKeySequence.Save, 'save_as', 'Create test file')
        self.toolBar.addAction(savePrnAction)

        unpackPrnAction = self.createAction('Unpack .prn', self.unpackPrnFile, QKeySequence.Open, 'analyze', 'Unpack prn file')
        self.toolBar.addAction(unpackPrnAction)

        editHeaderAction = self.createAction('Edit .prn', self.changeResolution, QKeySequence(Qt.CTRL+Qt.Key_E), 'edit', 'Change print resolution in prn file')
        self.toolBar.addAction(editHeaderAction)

        self.pix = QPixmap()
        self.pix.convertFromImage(QImage())
        self.grPixmap = QGraphicsPixmapItem(self.pix)
        self.scene.addItem(self.grPixmap)
#        self.view.scale(0.3, 0.3)

        self.setWindowTitle('PRN Creator {}'.format(self.ver))
        self.setWindowIcon(QIcon('icons/icon.png'))

        desktop = QApplication.desktop()
        rect = desktop.availableGeometry()
        pos = self.settings.value('window/pos', QPoint(0,0))
        if pos.x() < rect.left():
            x = rect.left()
        else:
            x = pos.x()
        if pos.y() < rect.top():
            y = rect.top()
        else:
            y = pos.y()

        self.resize(self.settings.value('window/size', QSize(640, 480)))
        self.move(x, y)
        self.setWindowState(Qt.WindowState(int(self.settings.value('window/state', 0))))

    def setScale(self, scaleBool):
        if not self.scene.sceneRect().size().isEmpty():
            scaleX = self.view.size().width()/self.scene.sceneRect().size().width()
            scaleY = self.view.size().height()/self.scene.sceneRect().size().height()
            self.view.resetMatrix()
            if scaleBool:
                self.view.scale(min(scaleX, scaleY), min(scaleX, scaleY))

    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        for file in event.mimeData().urls():
            fi = QFileInfo(QFile(file.toLocalFile()))
            if fi.suffix() in self.acceptedFormats:
                self.addImageToTable(fi.filePath(), self.table.rowCount())
            else:
                self.statusBar.showMessage('File format of {} not supported'.format(fi.fileName()), 5000)
        event.accept()

    def createColorComboBox(self, index):
        self.colorWidgets[index] = QComboBox()
        for color in self.listColors:
            self.colorWidgets[index].addItem(QIcon('icons/colors/{}.png'.format(self.colors[color] if color != 'Light Cyan' and color != 'Light Magenta' and color != 'Violet'  and color != 'Medium Black' and color != 'Light Gray' else self.colors[color]+'_')), color)
        return self.colorWidgets[index]

    def createLevelComboBox(self, index):
        self.levelWidgets[index] = QComboBox()
        for level in range(1, 2**self.gsLevel):
            self.levelWidgets[index].addItem("Level {}".format(level), level)
        return self.levelWidgets[index]

    def tableCellActivated(self, row, column):
        self.scene.removeItem(self.grPixmap)
        image = self.table.item(row, 0).data(Qt.UserRole)
        self.pix.convertFromImage(image)
        self.grPixmap = QGraphicsPixmapItem(self.pix)
        self.scene.addItem(self.grPixmap)
        self.scene.setSceneRect(0,0, float(image.width()), float(image.height()))
        self.setScale(self.chkBoxScale.isChecked())

    def closeEvent(self, event):
        self.settings.setValue('window/size', self.size())
        self.settings.setValue('window/pos', self.pos())
        self.settings.setValue('window/state', self.windowState())
        event.accept()

    def addFile(self, index):

        file = QFileDialog.getOpenFileName(self, 'Load image', directory=self.settings.value('directories/dir_open',QDir.currentPath()), filter='Bitmap graphics (*.tif *.bmp);;All (*)')
        if not file:
            return
        self.settings.setValue('directories/dir_open', QFileInfo(file).path())
        self.settings.sync()
        self.addImageToTable(file, index)

    def addImageToTable(self, file, index):
        image = QImage(file)
        if image.format() == QImage.Format_Mono:
            self.table.insertRow(index)
            self.table.setItem(index, 0, QTableWidgetItem(QFileInfo(file).fileName()))
            self.table.item(index, 0).setFlags(Qt.ItemIsUserCheckable|Qt.ItemIsEnabled|Qt.ItemIsSelectable)
            self.table.item(index,0).setData(Qt.UserRole, image)
            self.table.item(index, 0).setToolTip(file)
            self.table.setCellWidget(index, 1, self.createColorComboBox(index))
            self.table.setCellWidget(index, 2, self.createLevelComboBox(index))
            self.table.setCurrentCell(index, 0)
            self.table.setItem(index, 3, QTableWidgetItem("{}x{}".format(image.width(), image.height())))
            self.table.item(index, 3).setFlags(Qt.ItemIsSelectable)
        else:
            self.statusBar.showMessage("Tiff file not monochrome ({})".format(image.format()))


    def removeFile(self, index):
        self.table.removeRow(index)
        self.table.setCurrentCell(index-1, 0)
        try:
            del self.colorWidgets[index]
            del self.levelWidgets[index]
        except (KeyError, IndexError) as err:
            pass

    def fillCmbGSLevel(self, index):
        """
        Фунция для заполнения списка уровней значениями. Вызывается при выборе уровня GS
        """
        self.gsLevel = self.levelGS[self.cmbGS.currentText()]
        for widget in self.levelWidgets:
            if self.levelWidgets[widget] is not None:
                self.levelWidgets[widget].clear()
            for level in range(1, 2**self.gsLevel):
                self.levelWidgets[widget].addItem("Level {}".format(level), level)


    def savePrnFile(self):
        width = 0
        height = 0
        images = {}
        for index in range(self.table.rowCount()):
            image = self.table.item(index, 0).data(Qt.UserRole)
            color = self.colors[self.table.cellWidget(index, 1).currentText()]
            if color not in images:
                images[color] = {}

            levelCmb = self.table.cellWidget(index, 2)
            level = levelCmb.itemData(levelCmb.currentIndex())
            if level in images[color]:
                QMessageBox.warning(self, "Error!", "Duplicated GS Level for color!")
                return
            else:
                images[color][level] = image

            if width != 0:
                if width != image.width():
                    QMessageBox.warning(self, "Error!", "Width of images does not match!")
                    return
            else:
                width = image.width()

            if height != 0:
                if height != image.height():
                    QMessageBox.warning(self, "Error!", "Height of images does not match!")
                    return
            else:
                height = image.height()

        fileName = QFileDialog.getSaveFileName(self, "Save .prn file", self.settings.value('directories/dir_save', QDir.currentPath()),
                "PRN Files (*.prn);;BIN Files (*.bin)")

        if not fileName:
            return

        file = QFile(fileName)
        if not file.open(QFile.WriteOnly):
            QMessageBox.warning(self, "Prn Creator", "Unable to write file {}:\n{}.".format(self.fileName, file.errorString()))
            return
        self.settings.setValue('directories/dir_save', QFileInfo(file).path())

        self.testName, status = QInputDialog.getText(self, "Test name", "Enter name of the test to write into file:", text="Test")

        if not status:
            QMessageBox.warning(self, "Prn Creator", "Name changed to 'Test'")
            self.testName = "Test"

        self.fileName = QFileInfo(file).fileName()
        self.out = QDataStream(file)
        self.out.setByteOrder(QDataStream.LittleEndian)

        colors = ''
        for color in images:
            colors+=color

        self.imgwidth = width
        self.imgheight = height
        self.bytesPerLine = int((self.gsLevel*self.imgwidth+31)/32)*4
        self.writeNewHeader(colors)

        pd = QProgressDialog('Writing file ...', 'Cancel', 0, self.imgheight-1, self)
        pd.setWindowTitle('Creating file...')
        pd.open()


        tmp = 0
        # Записывать данные будем построчно
        for row in range(self.imgheight):
            # обходим по порядку цветов, как расположены в файле
            for color in colors:
                # Добиваем строку до длины, кратной 32м байтам
                for column in range (int(self.bytesPerLine/self.gsLevel*8)):
                    shift = 7-column+int(column/8)*8
                    pixel = 0
                    # Упорядочиваем уровни (на всякий)
                    levels = sorted(list(images[color].keys()))
                    # Если изображение меньше длины строки, то
                    if column < self.imgwidth:
                        for level in levels:
                            # Выбираем максимальный уровень из тех, что указаны в данном пикселе на всех слоях
                            if images[color][level].pixel(column, row) == self.black:
                                pixel = level
                    tmp |= (pixel<<shift*self.gsLevel)
                    # Как только сдвиг обнулится, то записываем в файл
                    if not shift:
                        self.out.writeRawData(tmp.to_bytes(self.gsLevel, 'big'))
                        tmp=0
            pd.setValue(row)
            if pd.wasCanceled():
                pd.close()
                file.close()
                return
        pd.close()
        file.close()

        self.status.showMessage("File created successful", 7000)


    def writeNewHeader(self, colors):
        xdpi = int(self.xdpiCmbBox.currentText())
        ydpi = int(self.ydpiCmbBox.currentText())

        self.paperWidth = 0

        colorOrder = colors.encode()
        while len(colorOrder) < 16:
            colorOrder += bytes.fromhex('00')

        self.out.writeInt32(self.NEW_RIP_FILE)
        self.out.writeInt32(xdpi)
        self.out.writeInt32(ydpi)
        self.out.writeInt32(self.bytesPerLine)
        self.out.writeInt32(self.imgheight)
        self.out.writeInt32(self.imgwidth)
        self.out.writeInt32(self.paperWidth)
        self.out.writeInt32(len(colors))
        self.out.writeInt32(self.gsLevel)

        self.out.writeRawData(colorOrder)

        name = self.testName.encode(encoding='utf_16')[2:514] # Удаляем первые два байта сигнатуры UTF-16
        while len(name) < 256*2: # Дополняем до 256 символов WCHAR
            name += bytes.fromhex('00')
        self.out.writeRawData(name)

        self.out.writeInt32(0) # Зарезервировано

    def unpackPrnFile(self):
        fileName = QFileDialog.getOpenFileName(self, 'Load .prn file', directory=self.settings.value('unpacker/dir_open',QDir.currentPath()), filter='PRN files (*.prn *.bin);;All (*)')
        if not fileName:
            return
        file = QFile(fileName)
        if not file.open(QFile.ReadOnly):
            QMessageBox.warning(self, "Unpacker .prn", "Unable load file {}:\n{}.".format(fileName, file.errorString()))
            return
        self.settings.setValue('unpacker/dir_open', QFileInfo(file).path())
        data = QDataStream(file)
        data.setByteOrder(QDataStream.LittleEndian)
        file_name = QFileInfo(file).fileName()

        headerVersion = int.from_bytes(data.readRawData(4), 'little', signed=False)
        self.status.showMessage("Header Type: {:#X}".format(headerVersion))

        if headerVersion == self.NEW_RIP_FILE:
            xdpi = data.readInt32()
            ydpi = data.readInt32()
            bpl = data.readInt32()
            imgh = data.readInt32()
            imgw = data.readInt32()
            ppw = data.readInt32()
            colnum = data.readInt32()
            bitsperdrop = data.readInt32()
            colorOrder = data.readRawData(16).decode().replace(chr(0), '')
            colorList = list(colorOrder)
            name = b'\xff\xfe' + data.readRawData(512)
            name = name.decode('utf-16').replace(chr(0), '')
            zero = data.readInt32()
        elif headerVersion == self.STD_RIP_FILE:
            xdpi = data.readInt32()
            ydpi = data.readInt32()
            bpl = data.readInt32()
            imgh = data.readInt32()
            imgw = data.readInt32()
            ppw = data.readInt32()
            colnum = data.readInt32()
            bitsperdrop = data.readInt32()
            if not bitsperdrop:
                QMessageBox.warning(self, "Unpacker .prn", "File format not supported")
                return
            colorOrder = data.readRawData(16).decode().replace(chr(0), '')
            colorList = list(colorOrder)
            zero = data.readInt32()
            name = 'Not used in this format'
        elif headerVersion == self.CALDERA_RIP_FILE:
            xdpi = data.readInt32()
            ydpi = data.readInt32()
            bpl = data.readInt32()
            imgh = data.readInt32()
            imgw = data.readInt32()
            ppw = data.readInt32()
            colnum = data.readInt32()
            bitsperdrop = data.readInt32()
            if not bitsperdrop:
                QMessageBox.warning(self, "Unpacker .prn", "File format not supported")
                return
            colorOrder = data.readRawData(16).decode().replace(chr(0), '')
            colorList = list(colorOrder)
            zero = data.readInt32()
            name = 'Not used in this format'
        else:
            QMessageBox.warning(self, "Unpacker .prn", "File format not supported")
            return

        colors = {}
        for color in colorList:
            colors[color] = {}
            for level in range(1, 2**bitsperdrop):
                colors[color][level] = QImage(imgw, imgh , QImage.Format_Mono)
                colors[color][level].setColor(0, qRgb(255, 255, 255))
                colors[color][level].setColor(1, qRgb(0, 0, 0))
                colors[color][level].fill(0)

        pd = QProgressDialog('Analyze file ...', 'Cancel', 0, imgh-1, self)
        pd.setWindowTitle('Analyze ...')
        pd.open()

        mask = 0xFF>>(8-bitsperdrop) # Получаем маску для текущего количества бит на каплю
        for row in range(imgh):
            for color in colorList:
                for bytenum in range(0, bpl, bitsperdrop): # Получаем номер байта, с которого будем читать
                    byte = int.from_bytes(data.readRawData(bitsperdrop), 'big')
                    for pix in range(8):
                        shift = (7 - pix)*bitsperdrop
                        pixel = (byte>>shift)&mask
                        if pixel > 0:
                            numofpix = 8*bytenum/bitsperdrop + pix
                            if numofpix < imgw:
                                colors[color][pixel].setPixel(numofpix, row, 1)
            pd.setValue(row)
            if pd.wasCanceled():
                pd.close()
                file.close()
                return
        pd.close()
        file.close()

        catalog = QFileDialog.getExistingDirectory(self, directory=self.settings.value('unpacker/dir_save',QDir.currentPath()))
        if catalog is None:
            return
        self.settings.setValue('unpacker/dir_save', catalog)

        file = QFile("{}\{}_{}.txt".format(catalog, file_name, colorOrder))
        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(self, "Prn creator", "Unable create file {}:\n{}.".format(name, file.errorString()))
            return

        text = QTextStream(file)
        text << "Filename: {}\n".format(file_name)
        text << "Test Name: {}\n\n".format(name)
        text << "X Resolution: {} dpi\n".format(xdpi)
        text << "Y Resolution: {} dpi\n".format(ydpi)
        text << "Image Width: {} pix\n".format(imgw)
        text << "Image Height: {} pix\n\n".format(imgh)
        text << "Bits Per Drop: {}\n".format(bitsperdrop)
        text << "Bytes Per Line: {}\n\n".format(bpl)
        text << "Number Of Colors: {}\n".format(colnum)
        text << "Order Of Colors: {}\n\n".format(colorOrder)
        text << "Paper Width: {}\n".format(ppw)

        for color in colorList:
            for level in range(1, 2**bitsperdrop):
                colors[color][level].save("{}\{}.{}.gs{}.tif".format(catalog, file_name, "{}".format(color) if color.isupper() else "_{}".format(color), level))

        self.status.showMessage("Unpacking finished", 7000)

    def changeResolution(self):
        fileName = QFileDialog.getOpenFileName(self, 'Load .prn file', directory=self.settings.value('unpacker/dir_open',QDir.currentPath()), filter='PRN files (*.prn *.bin);;All (*)')
        if not fileName:
            return
        file = QFile(fileName)
        if not file.open(QFile.ReadWrite):
            QMessageBox.warning(self, "Unpacker .prn", "Unable load file {}:\n{}.".format(fileName, file.errorString()))
            return
        self.settings.setValue('unpacker/dir_open', QFileInfo(file).path())
        data = QDataStream(file)
        data.setByteOrder(QDataStream.LittleEndian)
        headerVersion = data.readInt32()
        xdpi = data.readInt32()
        ydpi = data.readInt32()

        dialog = ChangeResolutionDialog(xdpi, ydpi, self)
        if dialog.exec_():
            file.seek(4)
            data.writeInt32(dialog.sbXdpi.value())
            data.writeInt32(dialog.sbYdpi.value())
            self.status.showMessage("Resolution changed successful", 7000)
        file.close()


    def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal='triggered()'):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon('icons/{}.png'.format(icon)))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot)
        if checkable:
            action.setCheckable(True)
        return action
Пример #4
0
class MainWindow(QMainWindow):
    STD_RIP_FILE = 0x1111FFFF
    NEW_RIP_FILE = 0x2222FFFF
    CALDERA_RIP_FILE = 0xFFFF1111

    ver = '1.3.10'

    colors = {
        'Black': 'K',
        'Cyan': 'C',
        'Magenta': 'M',
        'Yellow': 'Y',
        'Light Cyan': 'c',
        'Light Magenta': 'm',
        'White': 'W',
        'Varnish': 'V',
        'Orange': 'O',
        'Green': 'G',
        'Gold': 'D',
        'Silver': 'S',
        'Medium Black': 'k',
        'Light Gray': 'g',
        'Red': 'R',
        'Blue': 'B',
        'Violet': 'v',
        'Spot Color 1': '1',
        'Spot Color 2': '2',
        'Spot Color 3': '3',
        'Spot Color 4': '4',
        'Spot Color 5': '5',
        'Spot Color 6': '6',
        'Spot Color 7': '7',
        'Spot Color 8': '8',
        'Spot Color 9': '9'
    }

    listColors = [
        'Black', 'Cyan', 'Magenta', 'Yellow', 'Light Cyan', 'Light Magenta',
        'White', 'Varnish', 'Orange', 'Red', 'Green', 'Blue', 'Violet', 'Gold',
        'Silver', 'Medium Black', 'Light Gray', 'Spot Color 1', 'Spot Color 2',
        'Spot Color 3', 'Spot Color 4', 'Spot Color 5', 'Spot Color 6',
        'Spot Color 7', 'Spot Color 8', 'Spot Color 9'
    ]

    levelGS = {
        '1 bit per drop (Binary)': 1,
        '2 bit per drop': 2,
        '4 bit per drop (not used)': 4
    }

    acceptedFormats = ['tif', 'bmp']

    listGSLevel = ['1 bit per drop (Binary)',
                   '2 bit per drop']  #,'4 bit per drop (not used)']

    black = qRgb(0, 0, 0)

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

        #        self.stackLayout = QStackedLayout()

        self.settings = QSettings('settings.ini', QSettings.IniFormat)

        self.vLayout = QVBoxLayout()
        self.toolBar = QToolBar('mainToolBar')
        self.addToolBar(Qt.RightToolBarArea, self.toolBar)
        self.centralWidget = QSplitter(Qt.Vertical)
        self.setCentralWidget(self.centralWidget)

        self.status = self.statusBar()

        self.colorWidgets = {}
        self.levelWidgets = {}

        self.cmbGS = QComboBox()
        self.cmbGS.addItems(self.listGSLevel)
        self.connect(self.cmbGS, SIGNAL("currentIndexChanged(int)"),
                     self.fillCmbGSLevel)
        self.gsLevel = self.levelGS[self.cmbGS.currentText()]

        self.xdpiCmbBox = QComboBox()
        self.xdpiCmbBox.addItems(['720', '600', '360', '300', '180', '150'])

        self.ydpiCmbBox = QComboBox()
        self.ydpiCmbBox.addItems(['360', '300', '180', '150', '90', '75'])

        self.table = QTableWidget(0, 4)
        self.connect(self.table, SIGNAL("cellClicked(int, int)"),
                     self.tableCellActivated)
        self.table.setHorizontalHeaderLabels(
            ['Filename', 'Color', 'GS Level', 'Size'])
        self._table = TableWidget(self.table, self.cmbGS, self.xdpiCmbBox,
                                  self.ydpiCmbBox)
        self.connect(self._table, SIGNAL("insertRowAfter(int)"), self.addFile)
        self.connect(self._table, SIGNAL("removeRowAt(int)"), self.removeFile)
        #        self._table.setAcceptDrops(True)
        self.table.setAcceptDrops(True)

        self.scene = QGraphicsScene()
        self.view = QGraphicsView(self.scene)

        self.scale = False
        self.chkBoxScale = QCheckBox("See the Whole")
        self.connect(self.chkBoxScale, SIGNAL("toggled(bool)"), self.setScale)

        self.centralWidget.addWidget(self._table)

        vbox = QVBoxLayout()
        vbox.addWidget(self.chkBoxScale)
        vbox.addWidget(self.view)
        widget = QWidget()
        widget.setLayout(vbox)
        self.centralWidget.addWidget(widget)
        #        self.centralWidget.addWidget(self.statusBar)

        savePrnAction = self.createAction('Create .prn', self.savePrnFile,
                                          QKeySequence.Save, 'save_as',
                                          'Create test file')
        self.toolBar.addAction(savePrnAction)

        unpackPrnAction = self.createAction('Unpack .prn', self.unpackPrnFile,
                                            QKeySequence.Open, 'analyze',
                                            'Unpack prn file')
        self.toolBar.addAction(unpackPrnAction)

        editHeaderAction = self.createAction(
            'Edit .prn', self.changeResolution,
            QKeySequence(Qt.CTRL + Qt.Key_E), 'edit',
            'Change print resolution in prn file')
        self.toolBar.addAction(editHeaderAction)

        self.pix = QPixmap()
        self.pix.convertFromImage(QImage())
        self.grPixmap = QGraphicsPixmapItem(self.pix)
        self.scene.addItem(self.grPixmap)
        #        self.view.scale(0.3, 0.3)

        self.setWindowTitle('PRN Creator {}'.format(self.ver))
        self.setWindowIcon(QIcon('icons/icon.png'))

        desktop = QApplication.desktop()
        rect = desktop.availableGeometry()
        pos = self.settings.value('window/pos', QPoint(0, 0))
        if pos.x() < rect.left():
            x = rect.left()
        else:
            x = pos.x()
        if pos.y() < rect.top():
            y = rect.top()
        else:
            y = pos.y()

        self.resize(self.settings.value('window/size', QSize(640, 480)))
        self.move(x, y)
        self.setWindowState(
            Qt.WindowState(int(self.settings.value('window/state', 0))))

    def setScale(self, scaleBool):
        if not self.scene.sceneRect().size().isEmpty():
            scaleX = self.view.size().width() / self.scene.sceneRect().size(
            ).width()
            scaleY = self.view.size().height() / self.scene.sceneRect().size(
            ).height()
            self.view.resetMatrix()
            if scaleBool:
                self.view.scale(min(scaleX, scaleY), min(scaleX, scaleY))

    def dragEnterEvent(self, event):
        event.acceptProposedAction()

    def dropEvent(self, event):
        for file in event.mimeData().urls():
            fi = QFileInfo(QFile(file.toLocalFile()))
            if fi.suffix() in self.acceptedFormats:
                self.addImageToTable(fi.filePath(), self.table.rowCount())
            else:
                self.statusBar.showMessage(
                    'File format of {} not supported'.format(fi.fileName()),
                    5000)
        event.accept()

    def createColorComboBox(self, index):
        self.colorWidgets[index] = QComboBox()
        for color in self.listColors:
            self.colorWidgets[index].addItem(
                QIcon('icons/colors/{}.png'.format(
                    self.colors[color]
                    if color != 'Light Cyan' and color != 'Light Magenta'
                    and color != 'Violet' and color != 'Medium Black'
                    and color != 'Light Gray' else self.colors[color] + '_')),
                color)
        return self.colorWidgets[index]

    def createLevelComboBox(self, index):
        self.levelWidgets[index] = QComboBox()
        for level in range(1, 2**self.gsLevel):
            self.levelWidgets[index].addItem("Level {}".format(level), level)
        return self.levelWidgets[index]

    def tableCellActivated(self, row, column):
        self.scene.removeItem(self.grPixmap)
        image = self.table.item(row, 0).data(Qt.UserRole)
        self.pix.convertFromImage(image)
        self.grPixmap = QGraphicsPixmapItem(self.pix)
        self.scene.addItem(self.grPixmap)
        self.scene.setSceneRect(0, 0, float(image.width()),
                                float(image.height()))
        self.setScale(self.chkBoxScale.isChecked())

    def closeEvent(self, event):
        self.settings.setValue('window/size', self.size())
        self.settings.setValue('window/pos', self.pos())
        self.settings.setValue('window/state', self.windowState())
        event.accept()

    def addFile(self, index):

        file = QFileDialog.getOpenFileName(
            self,
            'Load image',
            directory=self.settings.value('directories/dir_open',
                                          QDir.currentPath()),
            filter='Bitmap graphics (*.tif *.bmp);;All (*)')
        if not file:
            return
        self.settings.setValue('directories/dir_open', QFileInfo(file).path())
        self.settings.sync()
        self.addImageToTable(file, index)

    def addImageToTable(self, file, index):
        image = QImage(file)
        if image.format() == QImage.Format_Mono:
            self.table.insertRow(index)
            self.table.setItem(index, 0,
                               QTableWidgetItem(QFileInfo(file).fileName()))
            self.table.item(
                index, 0).setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled
                                   | Qt.ItemIsSelectable)
            self.table.item(index, 0).setData(Qt.UserRole, image)
            self.table.item(index, 0).setToolTip(file)
            self.table.setCellWidget(index, 1, self.createColorComboBox(index))
            self.table.setCellWidget(index, 2, self.createLevelComboBox(index))
            self.table.setCurrentCell(index, 0)
            self.table.setItem(
                index, 3,
                QTableWidgetItem("{}x{}".format(image.width(),
                                                image.height())))
            self.table.item(index, 3).setFlags(Qt.ItemIsSelectable)
        else:
            self.statusBar.showMessage("Tiff file not monochrome ({})".format(
                image.format()))

    def removeFile(self, index):
        self.table.removeRow(index)
        self.table.setCurrentCell(index - 1, 0)
        try:
            del self.colorWidgets[index]
            del self.levelWidgets[index]
        except (KeyError, IndexError) as err:
            pass

    def fillCmbGSLevel(self, index):
        """
        Фунция для заполнения списка уровней значениями. Вызывается при выборе уровня GS
        """
        self.gsLevel = self.levelGS[self.cmbGS.currentText()]
        for widget in self.levelWidgets:
            if self.levelWidgets[widget] is not None:
                self.levelWidgets[widget].clear()
            for level in range(1, 2**self.gsLevel):
                self.levelWidgets[widget].addItem("Level {}".format(level),
                                                  level)

    def savePrnFile(self):
        width = 0
        height = 0
        images = {}
        for index in range(self.table.rowCount()):
            image = self.table.item(index, 0).data(Qt.UserRole)
            color = self.colors[self.table.cellWidget(index, 1).currentText()]
            if color not in images:
                images[color] = {}

            levelCmb = self.table.cellWidget(index, 2)
            level = levelCmb.itemData(levelCmb.currentIndex())
            if level in images[color]:
                QMessageBox.warning(self, "Error!",
                                    "Duplicated GS Level for color!")
                return
            else:
                images[color][level] = image

            if width != 0:
                if width != image.width():
                    QMessageBox.warning(self, "Error!",
                                        "Width of images does not match!")
                    return
            else:
                width = image.width()

            if height != 0:
                if height != image.height():
                    QMessageBox.warning(self, "Error!",
                                        "Height of images does not match!")
                    return
            else:
                height = image.height()

        fileName = QFileDialog.getSaveFileName(
            self, "Save .prn file",
            self.settings.value('directories/dir_save', QDir.currentPath()),
            "PRN Files (*.prn);;BIN Files (*.bin)")

        if not fileName:
            return

        file = QFile(fileName)
        if not file.open(QFile.WriteOnly):
            QMessageBox.warning(
                self, "Prn Creator",
                "Unable to write file {}:\n{}.".format(self.fileName,
                                                       file.errorString()))
            return
        self.settings.setValue('directories/dir_save', QFileInfo(file).path())

        self.testName, status = QInputDialog.getText(
            self,
            "Test name",
            "Enter name of the test to write into file:",
            text="Test")

        if not status:
            QMessageBox.warning(self, "Prn Creator", "Name changed to 'Test'")
            self.testName = "Test"

        self.fileName = QFileInfo(file).fileName()
        self.out = QDataStream(file)
        self.out.setByteOrder(QDataStream.LittleEndian)

        colors = ''
        for color in images:
            colors += color

        self.imgwidth = width
        self.imgheight = height
        self.bytesPerLine = int((self.gsLevel * self.imgwidth + 31) / 32) * 4
        self.writeNewHeader(colors)

        pd = QProgressDialog('Writing file ...', 'Cancel', 0,
                             self.imgheight - 1, self)
        pd.setWindowTitle('Creating file...')
        pd.open()

        tmp = 0
        # Записывать данные будем построчно
        for row in range(self.imgheight):
            # обходим по порядку цветов, как расположены в файле
            for color in colors:
                # Добиваем строку до длины, кратной 32м байтам
                for column in range(int(self.bytesPerLine / self.gsLevel * 8)):
                    shift = 7 - column + int(column / 8) * 8
                    pixel = 0
                    # Упорядочиваем уровни (на всякий)
                    levels = sorted(list(images[color].keys()))
                    # Если изображение меньше длины строки, то
                    if column < self.imgwidth:
                        for level in levels:
                            # Выбираем максимальный уровень из тех, что указаны в данном пикселе на всех слоях
                            if images[color][level].pixel(column,
                                                          row) == self.black:
                                pixel = level
                    tmp |= (pixel << shift * self.gsLevel)
                    # Как только сдвиг обнулится, то записываем в файл
                    if not shift:
                        self.out.writeRawData(tmp.to_bytes(
                            self.gsLevel, 'big'))
                        tmp = 0
            pd.setValue(row)
            if pd.wasCanceled():
                pd.close()
                file.close()
                return
        pd.close()
        file.close()

        self.status.showMessage("File created successful", 7000)

    def writeNewHeader(self, colors):
        xdpi = int(self.xdpiCmbBox.currentText())
        ydpi = int(self.ydpiCmbBox.currentText())

        self.paperWidth = 0

        colorOrder = colors.encode()
        while len(colorOrder) < 16:
            colorOrder += bytes.fromhex('00')

        self.out.writeInt32(self.NEW_RIP_FILE)
        self.out.writeInt32(xdpi)
        self.out.writeInt32(ydpi)
        self.out.writeInt32(self.bytesPerLine)
        self.out.writeInt32(self.imgheight)
        self.out.writeInt32(self.imgwidth)
        self.out.writeInt32(self.paperWidth)
        self.out.writeInt32(len(colors))
        self.out.writeInt32(self.gsLevel)

        self.out.writeRawData(colorOrder)

        name = self.testName.encode(encoding='utf_16')[
            2:514]  # Удаляем первые два байта сигнатуры UTF-16
        while len(name) < 256 * 2:  # Дополняем до 256 символов WCHAR
            name += bytes.fromhex('00')
        self.out.writeRawData(name)

        self.out.writeInt32(0)  # Зарезервировано

    def unpackPrnFile(self):
        fileName = QFileDialog.getOpenFileName(
            self,
            'Load .prn file',
            directory=self.settings.value('unpacker/dir_open',
                                          QDir.currentPath()),
            filter='PRN files (*.prn *.bin);;All (*)')
        if not fileName:
            return
        file = QFile(fileName)
        if not file.open(QFile.ReadOnly):
            QMessageBox.warning(
                self, "Unpacker .prn",
                "Unable load file {}:\n{}.".format(fileName,
                                                   file.errorString()))
            return
        self.settings.setValue('unpacker/dir_open', QFileInfo(file).path())
        data = QDataStream(file)
        data.setByteOrder(QDataStream.LittleEndian)
        file_name = QFileInfo(file).fileName()

        headerVersion = int.from_bytes(data.readRawData(4),
                                       'little',
                                       signed=False)
        self.status.showMessage("Header Type: {:#X}".format(headerVersion))

        if headerVersion == self.NEW_RIP_FILE:
            xdpi = data.readInt32()
            ydpi = data.readInt32()
            bpl = data.readInt32()
            imgh = data.readInt32()
            imgw = data.readInt32()
            ppw = data.readInt32()
            colnum = data.readInt32()
            bitsperdrop = data.readInt32()
            colorOrder = data.readRawData(16).decode().replace(chr(0), '')
            colorList = list(colorOrder)
            name = b'\xff\xfe' + data.readRawData(512)
            name = name.decode('utf-16').replace(chr(0), '')
            zero = data.readInt32()
        elif headerVersion == self.STD_RIP_FILE:
            xdpi = data.readInt32()
            ydpi = data.readInt32()
            bpl = data.readInt32()
            imgh = data.readInt32()
            imgw = data.readInt32()
            ppw = data.readInt32()
            colnum = data.readInt32()
            bitsperdrop = data.readInt32()
            if not bitsperdrop:
                QMessageBox.warning(self, "Unpacker .prn",
                                    "File format not supported")
                return
            colorOrder = data.readRawData(16).decode().replace(chr(0), '')
            colorList = list(colorOrder)
            zero = data.readInt32()
            name = 'Not used in this format'
        elif headerVersion == self.CALDERA_RIP_FILE:
            xdpi = data.readInt32()
            ydpi = data.readInt32()
            bpl = data.readInt32()
            imgh = data.readInt32()
            imgw = data.readInt32()
            ppw = data.readInt32()
            colnum = data.readInt32()
            bitsperdrop = data.readInt32()
            if not bitsperdrop:
                QMessageBox.warning(self, "Unpacker .prn",
                                    "File format not supported")
                return
            colorOrder = data.readRawData(16).decode().replace(chr(0), '')
            colorList = list(colorOrder)
            zero = data.readInt32()
            name = 'Not used in this format'
        else:
            QMessageBox.warning(self, "Unpacker .prn",
                                "File format not supported")
            return

        colors = {}
        for color in colorList:
            colors[color] = {}
            for level in range(1, 2**bitsperdrop):
                colors[color][level] = QImage(imgw, imgh, QImage.Format_Mono)
                colors[color][level].setColor(0, qRgb(255, 255, 255))
                colors[color][level].setColor(1, qRgb(0, 0, 0))
                colors[color][level].fill(0)

        pd = QProgressDialog('Analyze file ...', 'Cancel', 0, imgh - 1, self)
        pd.setWindowTitle('Analyze ...')
        pd.open()

        mask = 0xFF >> (
            8 - bitsperdrop
        )  # Получаем маску для текущего количества бит на каплю
        for row in range(imgh):
            for color in colorList:
                for bytenum in range(
                        0, bpl, bitsperdrop
                ):  # Получаем номер байта, с которого будем читать
                    byte = int.from_bytes(data.readRawData(bitsperdrop), 'big')
                    for pix in range(8):
                        shift = (7 - pix) * bitsperdrop
                        pixel = (byte >> shift) & mask
                        if pixel > 0:
                            numofpix = 8 * bytenum / bitsperdrop + pix
                            if numofpix < imgw:
                                colors[color][pixel].setPixel(numofpix, row, 1)
            pd.setValue(row)
            if pd.wasCanceled():
                pd.close()
                file.close()
                return
        pd.close()
        file.close()

        catalog = QFileDialog.getExistingDirectory(
            self,
            directory=self.settings.value('unpacker/dir_save',
                                          QDir.currentPath()))
        if catalog is None:
            return
        self.settings.setValue('unpacker/dir_save', catalog)

        file = QFile("{}\{}_{}.txt".format(catalog, file_name, colorOrder))
        if not file.open(QFile.WriteOnly | QFile.Text):
            QMessageBox.warning(
                self, "Prn creator",
                "Unable create file {}:\n{}.".format(name, file.errorString()))
            return

        text = QTextStream(file)
        text << "Filename: {}\n".format(file_name)
        text << "Test Name: {}\n\n".format(name)
        text << "X Resolution: {} dpi\n".format(xdpi)
        text << "Y Resolution: {} dpi\n".format(ydpi)
        text << "Image Width: {} pix\n".format(imgw)
        text << "Image Height: {} pix\n\n".format(imgh)
        text << "Bits Per Drop: {}\n".format(bitsperdrop)
        text << "Bytes Per Line: {}\n\n".format(bpl)
        text << "Number Of Colors: {}\n".format(colnum)
        text << "Order Of Colors: {}\n\n".format(colorOrder)
        text << "Paper Width: {}\n".format(ppw)

        for color in colorList:
            for level in range(1, 2**bitsperdrop):
                colors[color][level].save("{}\{}.{}.gs{}.tif".format(
                    catalog, file_name, "{}".format(color)
                    if color.isupper() else "_{}".format(color), level))

        self.status.showMessage("Unpacking finished", 7000)

    def changeResolution(self):
        fileName = QFileDialog.getOpenFileName(
            self,
            'Load .prn file',
            directory=self.settings.value('unpacker/dir_open',
                                          QDir.currentPath()),
            filter='PRN files (*.prn *.bin);;All (*)')
        if not fileName:
            return
        file = QFile(fileName)
        if not file.open(QFile.ReadWrite):
            QMessageBox.warning(
                self, "Unpacker .prn",
                "Unable load file {}:\n{}.".format(fileName,
                                                   file.errorString()))
            return
        self.settings.setValue('unpacker/dir_open', QFileInfo(file).path())
        data = QDataStream(file)
        data.setByteOrder(QDataStream.LittleEndian)
        headerVersion = data.readInt32()
        xdpi = data.readInt32()
        ydpi = data.readInt32()

        dialog = ChangeResolutionDialog(xdpi, ydpi, self)
        if dialog.exec_():
            file.seek(4)
            data.writeInt32(dialog.sbXdpi.value())
            data.writeInt32(dialog.sbYdpi.value())
            self.status.showMessage("Resolution changed successful", 7000)
        file.close()

    def createAction(self,
                     text,
                     slot=None,
                     shortcut=None,
                     icon=None,
                     tip=None,
                     checkable=False,
                     signal='triggered()'):
        action = QAction(text, self)
        if icon is not None:
            action.setIcon(QIcon('icons/{}.png'.format(icon)))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot)
        if checkable:
            action.setCheckable(True)
        return action