def load_from_file(self): # producer slot self.ui.label.setText("Select an image file") file_name, t = QFileDialog.getOpenFileName(self, None, None, "Images (*.png *.jpg)") if not file_name: return image = QImage() if not image.load(file_name): self.ui.label.setText( "Selected file is not an image, please select another.") return self.ui.label.setPixmap(QPixmap.fromImage(image)) # Get the image data: buf = QBuffer() buf.open(QBuffer.ReadWrite) out = QDataStream(buf) out << image try: from prodcon_ipc.producer_ipc import ScopedProducer with ScopedProducer(self.producer_ipc, buf.size()) as sp: # Copy image data from buf into shared memory area: sp.data()[:sp.size()] = buf.data().data()[:sp.size()] except Exception as err: self.ui.label.setText(str(err)) if SHARED_STRUCT == 1: # Read from shared memory, increase value and write it back: if self.shmem_config.isAttached() or self.shmem_config.attach(): if self.shmem_config.lock(): counter, stop_flag, _ = struct.unpack( STRUCT_FORMAT, self.shmem_config.constData()) data = struct.pack(STRUCT_FORMAT, counter + 1, stop_flag, str(os.path.basename(file_name)[:30])) size = min(struct.calcsize(STRUCT_FORMAT), self.shmem_config.size()) self.shmem_config.data()[:size] = data[:size] self.shmem_config.unlock() if stop_flag: # stop producing? logzero.logger.info( "Consumer requested to stop the production.") sys.exit(0) else: logzero.logger.error("unable to lock " + self.shmem_config.key()) #self.shmem_config.detach() else: logzero.logger.error("unable to attach " + self.shmem_config.key())
def sendMessage(self): textbuf = QBuffer() textbuf.open(QBuffer.ReadWrite) messageText = QDataStream(textbuf) messageText << self.ui.messageBox.text size = textbuf.size() if not self.shareMemory.create(size): return size = min(self.shareMemory.size(), size) self.sharedMemory.lock()
def loadFromFile(self): """ This slot function is called when the "Load Image From File..." button is pressed on the firs Dialog process. First, it tests whether the process is already connected to a shared memory segment and, if so, detaches from that segment. This ensures that we always start the example from the beginning if we run it multiple times with the same two Dialog processes. After detaching from an existing shared memory segment, the user is prompted to select an image file. The selected file is loaded into a QImage. The QImage is displayed in the Dialog and streamed into a QBuffer with a QDataStream. Next, it gets a new shared memory segment from the system big enough to hold the image data in the QBuffer, and it locks the segment to prevent the second Dialog process from accessing it. Then it copies the image from the QBuffer into the shared memory segment. Finally, it unlocks the shared memory segment so the second Dialog process can access it. After self function runs, the user is expected to press the "Load Image from Shared Memory" button on the second Dialog process. """ if self.sharedMemory.isAttached(): self.detach() self.ui.label.setText("Select an image file") fileName, _ = QFileDialog.getOpenFileName( self, None, None, "Images (*.png *.xpm *.jpg)") image = QImage() if not image.load(fileName): self.ui.label.setText( "Selected file is not an image, please select another.") return self.ui.label.setPixmap(QPixmap.fromImage(image)) # Load into shared memory. buf = QBuffer() buf.open(QBuffer.ReadWrite) out = QDataStream(buf) out << image size = buf.size() if not self.sharedMemory.create(size): self.ui.label.setText("Unable to create shared memory segment.") return size = min(self.sharedMemory.size(), size) self.sharedMemory.lock() # Copy image data from buf into shared memory area. self.sharedMemory.data()[:] = buf.data().data() self.sharedMemory.unlock()
def render_to_file(self, res, file_object): """ Renders the image into a File resource. Returns the size of the data that has been written. """ qt_format = self.format # this may not be constant due to processEvents() image = self.render(res) q_buffer = QBuffer() image.save(q_buffer, qt_format) output = q_buffer.buffer().data() if type(file_object) is not _io.BufferedRandom: output = str(output) file_object.write(output) return q_buffer.size()
def loadFromFile(self): """ This slot function is called when the "Load Image From File..." button is pressed on the firs Dialog process. First, it tests whether the process is already connected to a shared memory segment and, if so, detaches from that segment. This ensures that we always start the example from the beginning if we run it multiple times with the same two Dialog processes. After detaching from an existing shared memory segment, the user is prompted to select an image file. The selected file is loaded into a QImage. The QImage is displayed in the Dialog and streamed into a QBuffer with a QDataStream. Next, it gets a new shared memory segment from the system big enough to hold the image data in the QBuffer, and it locks the segment to prevent the second Dialog process from accessing it. Then it copies the image from the QBuffer into the shared memory segment. Finally, it unlocks the shared memory segment so the second Dialog process can access it. After self function runs, the user is expected to press the "Load Image from Shared Memory" button on the second Dialog process. """ if self.sharedMemory.isAttached(): self.detach() self.ui.label.setText("Select an image file") fileName, _ = QFileDialog.getOpenFileName(self, None, None, "Images (*.png *.xpm *.jpg)") image = QImage() if not image.load(fileName): self.ui.label.setText( "Selected file is not an image, please select another.") return self.ui.label.setPixmap(QPixmap.fromImage(image)) # Load into shared memory. buf = QBuffer() buf.open(QBuffer.ReadWrite) out = QDataStream(buf) out << image size = buf.size() if not self.sharedMemory.create(size): self.ui.label.setText("Unable to create shared memory segment.") return size = min(self.sharedMemory.size(), size) self.sharedMemory.lock() # Copy image data from buf into shared memory area. self.sharedMemory.data()[:] = buf.data().data() self.sharedMemory.unlock()
class MayRenderer(QWidget): shouldSave = pyqtSignal() texsize = 512 samplerate = 44100 shaderHeader = '#version 130\nuniform float iTexSize;\nuniform float iBlockOffset;\nuniform float iSampleRate;\n\n' def __init__(self, parent): super().__init__() self.parent = parent self.blocksize = (self.texsize * self.texsize) / self.samplerate self.initState() self.initUI() self.initAudio() def initState(self): self.playing = False self.initVolume = 1 self.useWatchFile = False self.watchFileName = '' self.storeCodeIfNotWatching = '' self.useSynDump = False self.synDrumName = '' self.synFileName = '' def initUI(self): self.mainLayout = QVBoxLayout() self.codeLayout = QVBoxLayout() self.codeButtonBar = QHBoxLayout() self.codeWatchFileBar = QHBoxLayout() self.renderBar = QHBoxLayout() self.playbackBar = QHBoxLayout() self.renderGroup = QGroupBox() self.renderGroupLayout = QVBoxLayout() self.renderGroupLayout.addLayout(self.renderBar) self.renderGroupLayout.addLayout(self.playbackBar) self.renderGroup.setLayout(self.renderGroupLayout) self.renderGroup.setObjectName("renderGroup") self.synGroupLayout = QHBoxLayout() self.synGroup = QGroupBox() self.synGroup.setLayout(self.synGroupLayout) self.synDumpCheckBox = QCheckBox('Dump as') self.synDrumNameBox = QLineEdit(self) self.synFileNameBox = QLineEdit(self) self.synFileButton = QPushButton('...') self.synGroupLayout.addWidget(self.synDumpCheckBox, 1) self.synGroupLayout.addWidget(self.synDrumNameBox, 2) self.synGroupLayout.addWidget(QLabel('in'), .1) self.synGroupLayout.addWidget(self.synFileNameBox, 5) self.synGroupLayout.addWidget(self.synFileButton, 0.1) self.synDumpCheckBox.stateChanged.connect(self.toggleSynDump) self.synDrumNameBox.setPlaceholderText('drumname') self.synDrumNameBox.textChanged.connect(self.setSynDrumName) self.synFileNameBox.setPlaceholderText('some aMaySyn .syn file') self.synFileNameBox.textChanged.connect(self.setSynFileName) self.synFileButton.setMaximumWidth(40) self.synFileButton.clicked.connect(self.chooseSynFile) self.codeGroup = QGroupBox() self.buttonCopy = QPushButton('↬ Clipboard', self) self.buttonCopy.clicked.connect(self.copyToClipboard) self.buttonPaste = QPushButton('Paste ↴', self) self.buttonPaste.clicked.connect(self.pasteClipboard) self.buttonClear = QPushButton('×', self) self.buttonClear.clicked.connect(self.clearEditor) self.codeEditor = QPlainTextEdit(self) self.codeEditor.setLineWrapMode(QPlainTextEdit.WidgetWidth) #self.codeEditor.setCenterOnScroll(True) #self.codeEditor.textChanged.formatEditor()) # this gives a recursion problem, but how to filter e.g. tabs? self.codeEditor.cursorPositionChanged.connect(self.updatePosLabel) self.codeEditor.setTabStopWidth(14) self.watchFileCheckBox = QCheckBox('watch file:', self) self.watchFileCheckBox.stateChanged.connect(self.toggleWatchFile) self.watchFileNameBox = QLineEdit(self) self.watchFileNameBox.setPlaceholderText( 'use GLSL code file instead of the above editor...') self.watchFileNameBox.textChanged.connect(self.setWatchFileName) self.buttonWatchFile = QPushButton('...', self) self.buttonWatchFile.setMaximumWidth(40) self.buttonWatchFile.clicked.connect(self.chooseWatchFile) self.renderButton = QPushButton(self) self.renderButton.clicked.connect(self.pressRenderShader) self.renderLengthBox = QDoubleSpinBox(self) self.renderLengthBox.setMinimum(0) self.renderLengthBox.setValue(4 * self.blocksize - .01) self.renderLengthBox.setSingleStep(self.blocksize) self.renderLengthBox.setSuffix(' sec') self.renderLengthBox.setToolTip('render length') self.renderBpmBox = QSpinBox(self) self.renderBpmBox.setRange(1, 999) self.renderBpmBox.setValue(160) self.renderBpmBox.setPrefix('BPM ') self.renderBpmBox.setToolTip('--> determines SPB') self.playbackVolumeSlider = QSlider(Qt.Horizontal) self.playbackVolumeSlider.setMaximum(100) self.playbackVolumeSlider.setValue(self.initVolume * 100) self.playbackVolumeSlider.setToolTip('volume') self.playbackVolumeSlider.sliderMoved.connect(self.setVolume) self.renderBar.addWidget(self.renderButton, 60) self.renderBar.addWidget(self.renderBpmBox, 20) self.renderBar.addWidget(self.renderLengthBox, 20) self.progressBar = QProgressBar(self) self.progressBar.setEnabled(False) self.pauseButton = QPushButton(self) self.pauseButton.setEnabled(False) self.pauseButton.clicked.connect(self.pressPauseButton) self.playbackBar.addWidget(self.progressBar, 60) self.playbackBar.addWidget(self.playbackVolumeSlider, 20) self.playbackBar.addWidget(self.pauseButton, 20) self.codeButtonBar.addWidget(self.buttonCopy) self.codeButtonBar.addWidget(self.buttonPaste) self.codeButtonBar.addWidget(self.buttonClear) self.codeWatchFileBar.addWidget(self.watchFileCheckBox) self.codeWatchFileBar.addWidget(self.watchFileNameBox) self.codeWatchFileBar.addWidget(self.buttonWatchFile) self.codeHeader = QHBoxLayout() self.codePosLabel = QLabel('(0,0)') self.codeHeader.addWidget(QLabel('GLSL code')) self.codeHeader.addStretch() self.codeHeader.addWidget(self.codePosLabel) self.codeLayout.addLayout(self.codeHeader) self.codeLayout.addLayout(self.codeButtonBar) self.codeLayout.addWidget(self.codeEditor) self.codeLayout.addLayout(self.codeWatchFileBar) self.codeGroup.setLayout(self.codeLayout) self.mainLayout.addWidget(self.synGroup) self.mainLayout.addWidget(self.codeGroup) self.mainLayout.addWidget(self.renderGroup) self.updatePlayingUI() self.setLayout(self.mainLayout) def updatePlayingUI(self, keepActive=False): self.renderButton.setText( 'shut the f**k up' if self.playing else 'send to hell') if not self.playing and not keepActive: self.progressBar.setValue(0) self.progressBar.setEnabled(self.playing if not keepActive else True) self.pauseButton.setEnabled(self.playing if not keepActive else True) self.pauseButton.setText('||' if ( self.playing and self.audiooutput.state() != QAudio.SuspendedState ) else '▶') def initAudio(self): self.audioformat = QAudioFormat() self.audioformat.setSampleRate(self.samplerate) self.audioformat.setChannelCount(2) self.audioformat.setSampleSize(32) self.audioformat.setCodec('audio/pcm') self.audioformat.setByteOrder(QAudioFormat.LittleEndian) self.audioformat.setSampleType(QAudioFormat.Float) # self.audiodeviceinfo = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice()) self.audiooutput = QAudioOutput(self.audioformat) self.audiooutput.setVolume(self.initVolume) def paste(self, source): self.codeEditor.clear() source = source.replace(4 * ' ', '\t').replace(3 * ' ', '\t') self.codeEditor.insertPlainText(source) #self.codeEditor.setFocus() #TODO: think about whether we want this self.codeEditor.ensureCursorVisible() def pasteClipboard(self): self.paste(self.shaderHeader + QApplication.clipboard().text()) def copyToClipboard(self): text = self.codeEditor.toPlainText().replace('\t', 4 * ' ') QApplication.clipboard().setText(text) def clearEditor(self): self.codeEditor.setPlainText('') self.codeEditor.setFocus() def updatePosLabel(self): cursor = self.codeEditor.textCursor() self.codePosLabel.setText( f'({cursor.blockNumber()},{cursor.positionInBlock()})') # def formatEditor(self): # plainText = self.codeEditor.toPlainText().replace('\t', 4*' ') # self.codeEditor.setPlainText(plainText) def toggleSynDump(self, state): self.useSynDump = (state == Qt.Checked) self.shouldSave.emit() def chooseSynFile(self): dialogResult, _ = QFileDialog.getSaveFileName( self, 'Choose SYN definition file', '', 'aMaySyn definition files (*.syn);;All files (*)') print(dialogResult) self.synFileNameBox.setText(dialogResult) self.synDumpCheckBox.setCheckState(Qt.Checked) if self.synFileName == '': self.synFileNameBox.setFocus() self.shouldSave.emit() def setSynFileName(self): self.synFileName = self.synFileNameBox.text() self.shouldSave.emit() def setSynDrumName(self): self.synDrumName = self.synDrumNameBox.text() self.shouldSave.emit() def setSynDumpParameters(self, useSynDump, synFileName, synDrumName): self.synDumpCheckBox.setChecked(useSynDump) self.synFileNameBox.setText(synFileName) self.synDrumNameBox.setText(synDrumName) def toggleWatchFile(self, state): if not self.useWatchFile and state == Qt.Checked: self.storeCodeIfNotWatching = self.codeEditor.toPlainText() self.useWatchFile = (state == Qt.Checked) self.codeEditor.setEnabled(not self.useWatchFile) if self.useWatchFile: self.showWatchFileInfo() else: self.codeEditor.setPlainText(self.storeCodeIfNotWatching) def chooseWatchFile(self): dialogResult = QFileDialog.getOpenFileName( self, 'Choose file with GLSL code', '', 'GLSL files (*.glsl);;All files (*)') print(dialogResult) self.watchFileNameBox.setText(dialogResult[0]) self.watchFileCheckBox.setCheckState(Qt.Checked) self.shouldSave.emit() def setWatchFileName(self): self.watchFileName = self.watchFileNameBox.text() self.showWatchFileInfo() def showWatchFileInfo(self): if self.useWatchFile: fileInfo = QFileInfo(self.watchFileName) infoText = 'use code from file:\n' + self.watchFileName + '\n' + ( '(exists)' if fileInfo.exists() else '(doesn\'t exist)') self.codeEditor.setPlainText(infoText) def pressRenderShader(self): self.playing = not self.playing if self.playing: self.renderShaderAndPlay() else: self.stopShader() def pressPauseButton(self): state = self.audiooutput.state() if state == QAudio.ActiveState: self.audiooutput.suspend() elif state == QAudio.SuspendedState: self.audiooutput.resume() self.updatePlayingUI(keepActive=True) def stopShader(self): self.audiooutput.stop() self.updatePlayingUI() def renderShaderAndPlay(self, file=None): self.playing = True self.updatePlayingUI() shaderSource = self.shaderHeader + """ uniform float SPB; void main() { float t = (iBlockOffset + gl_FragCoord.x + gl_FragCoord.y*iTexSize) / iSampleRate; t = floor(t*BITS.) / BITS.; vec2 s = .2 * vec2(sin(2.*3.14159*49.*t*(1.+t)*SPB*2.667)); // let's make it fun and squeaky vec2 v = floor((0.5+0.5*s)*65535.0); vec2 vl = mod(v,256.0)/255.0; vec2 vh = floor(v/256.0)/255.0; gl_FragColor = vec4(vl.x,vh.x,vl.y,vh.y); } """ # this is the SUPER FUN BITCRUSHER for the test shader nr_bits = randint(128, 8192) shaderSource = shaderSource.replace('BITS', str(nr_bits)) print(nr_bits, 'bits for the SUPER FUN BITCRUSHER in the test shader.') starttime = datetime.now() try: if self.useWatchFile: watchFile = QFile(self.watchFileName) if not watchFile.open(QFile.ReadOnly | QFile.Text): QMessageBox.warning( self, "Öhm... blöd.", "File öffnen ging nicht. Is genügend Pfeffer drauf?") self.playing = False self.updatePlayingUI() return textStream = QTextStream(watchFile) textStream.setCodec('utf-8') shaderSource = self.shaderHeader + textStream.readAll() else: code = self.codeEditor.toPlainText() if code: shaderSource = code except: raise uniforms = {} SPB = 60 / float(self.renderBpmBox.value()) uniforms.update({'SPB': SPB}) print(self.renderLengthBox.value()) try: duration = self.renderLengthBox.value() except: print('couldn\'t read duration field. take 10secs.') duration = 10 glwidget = SFXGLWidget(self, self.audioformat.sampleRate(), duration, self.texsize, moreUniforms=uniforms) glwidget.show() log = glwidget.newShader(shaderSource) print(log) self.music = glwidget.music floatmusic = glwidget.floatmusic glwidget.hide() glwidget.destroy() if self.music == None: return self.renderLengthBox.setValue(round(glwidget.duration_real, 2) - .01) self.bytearray = QByteArray(self.music) self.audiobuffer = QBuffer(self.bytearray) self.audiobuffer.open(QIODevice.ReadOnly) endtime = datetime.now() el = endtime - starttime print("Compile time: {:.3f}s".format(el.total_seconds())) self.audiooutput.stop() self.audiooutput.start(self.audiobuffer) self.audiooutput.setNotifyInterval(100) self.audiooutput.stateChanged.connect(self.updatePlayingUI) self.progressBar.setMaximum(self.audiobuffer.size()) self.audiooutput.notify.connect(self.proceedAudio) if file is not None: floatmusic_L = [] floatmusic_R = [] for n, sample in enumerate(floatmusic): if n % 2 == 0: floatmusic_L.append(sample) else: floatmusic_R.append(sample) floatmusic_stereo = np.transpose( np.array([floatmusic_L, floatmusic_R], dtype=np.float32)) wavfile.write(file, self.samplerate, floatmusic_stereo) def proceedAudio(self): # print(self.audiobuffer.pos() / self.audioformat.sampleRate()) self.progressBar.setValue(self.audiobuffer.pos()) if self.audiobuffer.atEnd(): self.audiooutput.stop() self.playing = False self.updatePlayingUI() def setVolume(self): self.audiooutput.setVolume(self.playbackVolumeSlider.value() * .01) def dumpInSynFile(self, drumatizeL, drumatizeR, envCode, releaseTime): if not self.synDumpCheckBox.isChecked(): return if self.synDrumName == '': print("specify a valid drum name!!") return if self.synFileName == '': print("specify a valid .syn filename!!") return if not path.exists(self.synFileName): open(self.synFileName, 'a').close() uniqueEnv = f'_{self.synDrumName}ENV' drumatizeL = drumatizeL.replace('__ENV', uniqueEnv) drumatizeR = drumatizeR.replace('__ENV', uniqueEnv) envCode = envCode.replace('__ENV', uniqueEnv).replace('\n', ' ') parLine = f'param include src="{envCode}"\n' synLine = f'maindrum {self.synDrumName} src="{drumatizeL}" srcr="{drumatizeR}" release={releaseTime}\n' print(parLine, '\n', synLine) tmpSynFile = 'tmp.syn' copyfile(self.synFileName, tmpSynFile) parWritten, synWritten = False, False with open(tmpSynFile, 'r') as synFileHandle: synFileLines = synFileHandle.readlines() with open(self.synFileName, 'w') as synFileHandle: for line in synFileLines: parseLine = line.strip('\n').split() if parseLine[0:2] == ['maindrum', self.synDrumName]: synFileHandle.write(synLine) synWritten = True elif parseLine[0:2] == ['param', 'include' ] and line.find(uniqueEnv) != -1: synFileHandle.write(parLine) parWritten = True else: synFileHandle.write(line) if not parWritten: synFileHandle.write('\n' + parLine) if not synWritten: synFileHandle.write('\n' + synLine) synFileHandle.close()
def producer_repetitive_scope_test( path, repetitions=1, delay=0): # producer with repetitive writes to shared memory import time from PyQt5.QtGui import QPainter, QPixmap, QColor, QFont, QPen from PyQt5.QtCore import Qt from prodcon_ipc.producer_ipc import ProducerIPC # FIXME: this is still buggy because when the images are not read sufficiently fast from the consumer (which # only (?) happens in the non-async Python consumer), image data gets corrupted (may be overwritten by the # producer?! but this SHOULD be prevented by the system semaphores and the producer/consumer sync...) producer_ipc = ProducerIPC(UNIQUE_SHARED_MEMORY_NAME, SHARED_MEMORY_KEY_FILE) for i in range(repetitions): image = QImage() if not image.load(path): logzero.logger.error("Unable to load image " + path) sys.exit(1) else: pm = QPixmap.fromImage( image) # .convertToFormat(QImage.Format_RGB32) p = QPainter() p.begin(pm) p.setPen(QPen(Qt.yellow)) p.setFont(QFont("Times", 20, QFont.Bold)) p.drawText(pm.rect(), Qt.AlignCenter, str(i + 1) + " of " + str(repetitions)) p.end() image = pm.toImage() # Load the image: buf = QBuffer() buf.open(QBuffer.ReadWrite) out = QDataStream(buf) out << image try: avail_size, mem_data = producer_ipc.begin(buf.size()) if avail_size < buf.size(): logzero.logger.warn("Couldn't get enough memory!") except RuntimeError as err: logzero.logger.error(str(err)) sys.exit(2) # Copy image data from buf into shared memory area: error_str = None try: mem_data[:avail_size] = buf.data().data()[:avail_size] except Exception as err: logzero.logger.error(str(err)) try: producer_ipc.end() except RuntimeError as err: logzero.logger.error(str(err)) sys.exit(3) if delay > 0: time.sleep(delay) logzero.logger.debug("Iteration " + str(i + 1) + " of " + str(repetitions) + " completed.") logzero.logger.info("All okay") # IMPORTANT: The object should NOT go out of scope until the consumer has read the data, so (also) dont call: # del producer_ipc # ...or let the app exit right away. That's why we do here: try: input( "Press Enter to exit but let the consumer read the image first...") except: sys.stdout.write("\n") pass sys.exit(0)
class Chunks(QObject): def __init__(self, parent: QObject = None, device: QIODevice = None): super().__init__(parent) self.device = QBuffer(self) if device is None else device self.chunks: [Chunk] = [] self.position = 0 self.size = 0 self.setIODevice(self.device) def setIODevice(self, device: QIODevice) -> bool: self.device = device status = self.device.open(QIODevice.ReadOnly) if status: self.size = self.device.size() self.device.close() else: # Fallback is an empty buffer self.size = 0 self.device = QBuffer(self) self.chunks.clear() self.position = 0 return status def data(self, position: int, maxSize: int = -1, highlighted: QByteArray = None) -> QByteArray: delta = 0 chunkIdx = 0 chunk = Chunk() buffer = QByteArray() if highlighted: highlighted.clear() if position >= self.size: return buffer if maxSize < 0: maxSize = self.size elif (position + maxSize) > self.size: maxSize = self.size - position self.device.open(QIODevice.ReadOnly) while maxSize > 0: chunk.absPos = sys.maxsize chunksLoopOngoing = True while chunkIdx < len(self.chunks) and chunksLoopOngoing: # In this section, we track changes before our required data and # we take the editdet data, if availible. ioDelta is a difference # counter to justify the read pointer to the original data, if # data in between was deleted or inserted. chunk = self.chunks[chunkIdx] if chunk.absPos > position: chunksLoopOngoing = False else: count = 0 chunkIdx += 1 chunkOfs = position - chunk.absPos if maxSize > chunk.data.size() - chunkOfs: count = chunk.data.size() - chunkOfs delta += CHUNK_SIZE - chunk.data.size() else: count = maxSize if count > 0: buffer += chunk.data.mid(chunkOfs, count) maxSize -= count position += count if highlighted: highlighted += chunk.dataChanged.mid( chunkOfs, count) if maxSize > 0 and position < chunk.absPos: byteCount = 0 if (chunk.absPos - position) > maxSize: byteCount = maxSize else: byteCount = chunk.absPos - position maxSize -= byteCount self.device.seek(position + delta) readBuffer = self.device.read(byteCount) buffer += readBuffer if highlighted: highlighted += QByteArray(readBuffer.size(), NORMAL) position += len(readBuffer) self.device.close() return buffer def write(self, device: QIODevice, position: int = 0, count: int = -1) -> bool: if count == -1: count = self.size status = device.open(QIODevice.WriteOnly) if status: for idx in range(position, count, BUFFER_SIZE): array = self.data(idx, BUFFER_SIZE) device.write(array) device.close() return status def setDataChanged(self, position: int, dataChanged: bool) -> None: pass def dataChanged(self, position: int) -> bool: pass def indexOf(self, array: QByteArray, _from: int) -> int: pass def lastIndexOf(self, array: QByteArray, _from: int) -> int: pass def insert(self, position: int, character: str) -> bool: pass def overwrite(self, position: int, character: str) -> bool: pass def removeAt(self, position: int) -> bool: pass def getPosition(self) -> int: pass def getSize(self) -> int: pass def __getitem__(self, item): pass