class PrintJobNeedAuthMessage(Message): def __init__(self, device) -> None: super().__init__( title="Screen authorization needed", text="Please tap Yes on Snapmaker touchscreen to continue.", lifetime=0, dismissable=True, use_inactivity_timer=False) self._device = device self.setProgress(-1) # self.addAction("", "Continue", "", "") # self.actionTriggered.connect(self._onCheck) self._gTimer = QTimer() self._gTimer.setInterval(1.5 * 1000) self._gTimer.timeout.connect(lambda: self._onCheck(None, None)) self.inactivityTimerStart.connect(self._startTimer) self.inactivityTimerStop.connect(self._stopTimer) def _startTimer(self): if self._gTimer and not self._gTimer.isActive(): self._gTimer.start() def _stopTimer(self): if self._gTimer and self._gTimer.isActive(): self._gTimer.stop() def _onCheck(self, messageId, actionId): self._device.checkAndStartUpload()
class PrintJobUploadProgressMessage(Message): def __init__(self, device): super().__init__(title="Sending Print Job", text="Uploading print job to printer:", progress=-1, lifetime=0, dismissable=False, use_inactivity_timer=False) self._device = device self._gTimer = QTimer() self._gTimer.setInterval(3 * 1000) self._gTimer.timeout.connect(lambda: self._heartbeat()) self.inactivityTimerStart.connect(self._startTimer) self.inactivityTimerStop.connect(self._stopTimer) def show(self): self.setProgress(0) super().show() def update(self, percentage: int) -> None: if not self._visible: super().show() self.setProgress(percentage) def _heartbeat(self): self._device.check_status() def _startTimer(self): if self._gTimer and not self._gTimer.isActive(): self._gTimer.start() def _stopTimer(self): if self._gTimer and self._gTimer.isActive(): self._gTimer.stop()
class Timer: """Class for working with GUI timers.""" UPDATE_TIMER_MS = 150 # update timer (milliseconds) def __init__(self): """Class initialization.""" self._update_timer = QTimer() def set_timer(self, func, timer_ms=UPDATE_TIMER_MS): """Sets function to timer. :param function func: function caller. :param int timer_ms: time in ms. """ self._update_timer.timeout.connect(func) self.start(timer_ms=timer_ms) def stop(self): """Stops timer if it's active.""" if self._update_timer.isActive(): self._update_timer.stop() def start(self, timer_ms=UPDATE_TIMER_MS): """Starts timer if it's inactive. :param int timer_ms: time in ms. """ if not self._update_timer.isActive(): self._update_timer.start(timer_ms)
class MyMainWinow(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super(MyMainWinow,self).__init__(parent) self.setupUi(self) self.counter = 0 self.m4_timer = QTimer() # 初始化定时器 self.m4_timer.timeout.connect(self.m4_ImagePlay) # 创建 定时器信号槽 self.m4_OpenVideo_Btn.clicked.connect(self.m4_OpenVideo) self.m4_CloseVideo_Btn.clicked.connect(self.m4_CloseVideo) self.m4_TakePhoto_Btn.clicked.connect(self.m4_TakePhoto) self.m4_SetSavePath_Btn.clicked.connect(self.m4_SetSaveDir) def m4_SetSaveDir(self): self.SavedefaultPath = QFileDialog.getExistingDirectory(self, '设置图像保存目录!', './') self.m4_LabelDir.setText(self.SavedefaultPath) self.m4_OpenVideo_Btn.setEnabled(True) self.m4_CloseVideo_Btn.setEnabled(True) self.m4_TakePhoto_Btn.setEnabled(True) def m4_OpenVideo(self): if self.m4_timer.isActive() == False: # 定时器m4_timer没有启动 self.m4_TakePhoto_Btn.setEnabled(True) self.capture = cv2.VideoCapture(1) # 相机初始化 self.m4_timer.start(30) # 启动定时器m4_timer else: reply = QMessageBox.information(self, '提示', '相机已打开,无需再打开!', QMessageBox.Ok, QMessageBox.Ok) def m4_CloseVideo(self): if self.m4_timer.isActive() == True: # 定时器m4_timer没有启动 self.m4_timer.stop() # 启动定时器m4_timer self.capture.release() self.m4_ShowImage.setPixmap(QtGui.QPixmap(":/pic/YY.jpg")) self.m4_TakePhoto_Btn.setEnabled(False) else: reply = QMessageBox.information(self, '提示', '相机已关闭,无需再关闭!', QMessageBox.Ok, QMessageBox.Ok) def m4_ImagePlay(self): ret, self.image = self.capture.read() frame = self.image.copy() self.m4_AnnImageShow(frame) # 标注图像显示 def m4_AnnImageShow(self, frame): frame = frame.copy() height, width, bytesPerComponent = frame.shape bytesPerLine = 3 * width cv2.cvtColor(frame, cv2.COLOR_BGR2RGB, frame) QImg = QImage(frame.data, width, height, bytesPerLine, QImage.Format_RGB888) pixmap = QPixmap.fromImage(QImg) self.m4_ShowImage.setPixmap(pixmap) def m4_TakePhoto(self): nameTime = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time())) cv2.imwrite(self.SavedefaultPath + '/' + str(self.counter) + '_' + nameTime + '.png', self.image) self.m4_SavePath.setText(self.SavedefaultPath + '/' + str(self.counter) + '_' + nameTime + '.png') self.counter += 1
class TankVisualisation(MovingEntityVisualisation): def __init__(self, father, tank: Tank): super().__init__(father, tank, TankTextures.Textures[tank.type](tank.owner)) self.can_shoot = True self.is_shooting = False self.shooting_cd = QTimer() self.cooldown = self.wrapping_object.cooldown self.shooting_cd.setInterval(self.wrapping_object.cooldown * 1000) self.shooting_cd.timeout.connect(self._drop_cd) self.show() self.bars = [HealthBar(self), CoolDownBar(self)] self.active_debuffs = [] def update_bars(self): for bar in self.bars: bar.update_param() def _drop_cd(self): self.can_shoot = True # print('can shoot!') self.shooting_cd.stop() def shoot(self): if self.can_shoot: bullet = BulletVisualisation(self.father, self.wrapping_object.shoot()) bullet.stackUnder(self) self.parent().bullets.add(bullet) self.can_shoot = False # print(self.wrapping_object.cooldown, self.cooldown) if self.wrapping_object.cooldown != self.cooldown: self.cooldown = self.wrapping_object.cooldown * 1000 self.shooting_cd.setInterval(self.cooldown) self.shooting_cd.start() def treat_debuffs(self): for debuff in self.wrapping_object.active_debuffs: self.active_debuffs.append(DebuffVisualisation(self, debuff)) self.wrapping_object.active_debuffs = [] def stop_tank(self): if not self.shooting_cd.isActive(): return self.shooting_cd.stop() for buff in self.active_debuffs: buff.duration.stop() buff.ticks.stop() def start_tank(self): if self.shooting_cd.isActive(): return self.shooting_cd.start() for buff in self.active_debuffs: buff.duration.start() buff.ticks.start()
class Ventana(QWidget): def __init__(self): super().__init__() self.progressBar = QProgressBar(self) self.progressBar.setGeometry(30, 40, 200, 25) self.btnStart = QPushButton('Start', self) self.btnStart.move(40, 80) self.btnStart.clicked.connect(self.startProgress) self.timer = QTimer() self.timer.timeout.connect(self.funcion) self.step = 0 def startProgress(self): if self.timer.isActive(): self.timer.stop() self.btnStart.setText('Start') else: self.timer.start(10) self.btnStart.setText('Stop ') def funcion(self): if self.step >= 100: self.timer.stop() self.btnStart.setText('Finish') return self.step += 0.5 self.progressBar.setValue(self.step)
class LineEdit(QLineEdit): def __init__(self, parent=None): super(LineEdit, self).__init__(parent) self.text_focus = False # Clicking automatically selects all text, this allows clicks and drag # to highlight part of a url better self.clicklength = QTimer() self.clicklength.setSingleShot(True) self.clicklength.setTimerType(Qt.PreciseTimer) def mousePressEvent(self, e): if not self.text_focus: self.clicklength.start(120) self.text_focus = True else: super(LineEdit, self).mousePressEvent(e) def mouseReleaseEvent(self, e): if self.clicklength.isActive(): self.selectAll() super(LineEdit, self).mouseReleaseEvent(e) def focusOutEvent(self, e): super(LineEdit, self).focusOutEvent(e) self.text_focus = False
class HashmarksPage(IPFSPage): def __init__(self, marksLocal, marksNetwork, parent=None): super(HashmarksPage, self).__init__('hashmarks.html', parent=parent) self.marksLocal = marksLocal self.marksNetwork = marksNetwork self.marksLocal.markAdded.connect( lambda path, mark: self.onMarksChanged()) self.marksLocal.feedMarkAdded.connect( lambda path, mark: self.onMarksChanged()) self.hashmarks = HashmarksHandler(self.marksLocal, self.marksNetwork, self) self.register('hashmarks', self.hashmarks) self.register('galacteek', GalacteekHandler(None)) self.timerRerender = QTimer() def onMarksChanged(self): ensure(self.render()) def onSharedMarksChanged(self): if self.timerRerender.isActive(): self.timerRerender.stop() self.timerRerender.start(2000) async def render(self): self.setHtml(await renderTemplate( self.template, ipfsConnParams=self.app.getIpfsConnectionParams(), marks=self.marksLocal, marksShared=self.marksNetwork), baseUrl=QUrl('qrc:/'))
class ErrorMessageFilter(QObject): """ In a parallel program, the same error may occur in several threads in close succession. For example, all slice views will notice a "filter too large" error simultaneously. This class collects error messages for a certain time (currently: 1000ms) and then displays each unique message only once. """ def __init__(self, parent): super(QObject, self).__init__(parent) self.messages = {} self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.setInterval(1000) self.timer.timeout.connect( self.timeout ) def showErrorMessage(self, caption, text): if not self.timer.isActive(): self.timer.start() self.messages[caption] = text def timeout(self): # Must copy now because the eventloop is allowed to run during QMessageBox.critical, below. # That is, self.messages might change while the loop is executing (not allowed). messages = copy.copy(self.messages) for caption, text in messages.items(): QMessageBox.critical(self.parent(), caption, text) self.messages = {}
class DelayedTimer(QObject): triggered = pyqtSignal(str) def __init__(self, parent=None, max_delay=2000, min_delay=500): super().__init__(parent) self.max_delay = max_delay self.min_delay = min_delay self.min_timer = QTimer(self) self.max_timer = QTimer(self) self.min_timer.timeout.connect(self.timeout) self.max_timer.timeout.connect(self.timeout) self.string: str = "" def timeout(self): self.min_timer.stop() self.max_timer.stop() self.triggered.emit(self.string) def trigger(self, p_str: str): self.string = p_str if not self.max_timer.isActive(): self.max_timer.start(self.max_delay) self.min_timer.stop() self.min_timer.start(self.min_delay)
class trainerwindow(QtWidgets.QMainWindow,Ui_trainer): def __init__(self): super().__init__() self.setupUi(self) self.progressBar.setValue(0) self.timer_back2root = QTimer() def start(self): # display current works #start threading self.thread=Mythread(self) self.thread.change_value.connect(self.set_val) self.thread.start() def set_val(self,val): if val==-1: #print('get back to root') if not self.timer_back2root.isActive(): # start timer self.timer_back2root.start(20) else: # set progress bar self.progressBar.setValue((val + 1) * (100 / total)) self.pictures_info.setText('processing image: ' + str(val + 1) +' / '+ str(total)) if val==total: self.pictures_info.setText('saving file to the system')
class InputArea(QWidget): update_labels = pyqtSignal() clear_labels = pyqtSignal() def __init__(self): super().__init__() self.timer = QTimer() self.timer.setInterval(250) self.timer.setSingleShot(True) self.timer.timeout.connect(self.timeout) self.click_count = 0 self.message = '' self.temp = '' self.setAutoFillBackground(True) p = self.palette() p.setColor(self.backgroundRole(), Qt.lightGray) self.setPalette(p) self.outputMorse = QLabel() self.outputMorse.setText('Morse Code: ') self.outputConverted = QLabel() self.outputConverted.setText('Conv. Text: ') font = QtGui.QFont("Consolas", 10) font.setStyleHint(QtGui.QFont.TypeWriter) self.outputMorse.setFont(font) self.outputConverted.setFont(font) self.clearButton = QPushButton('Clear All') self.clearButton.clicked.connect(self.sendClearSignal) def mousePressEvent(self, event): self.click_count += 1 if event.button() == Qt.LeftButton: self.temp = '.' if event.button() == Qt.RightButton: self.temp = '*' if not self.timer.isActive(): self.timer.start() def timeout(self): if self.click_count > 1: if self.temp == '*': self.message += '**' else: self.message += '-' else: self.message += self.temp self.click_count = 0 self.update_labels.emit() def sendClearSignal(self): self.clear_labels.emit() def getMessage(self): return self.message def printMessage(self): print(self.message)
class MiTimers(QObject): senal_actualizar = pyqtSignal(int, str) def __init__(self, i, tiempo): super().__init__() self.indice = i self.tiempo = tiempo self.indice_actual = 0 self.timer = QTimer() self.timer.setInterval(tiempo * 1000) self.timer.timeout.connect(self.enviar_dato) def enviar_dato(self): if self.indice_actual < 9: self.senal_actualizar.emit(self.indice, str(self.indice_actual)) self.indice_actual += 1 else: self.senal_actualizar.emit(self.indice, 'Status: timer terminado') self.timer.stop() def comenzar(self): self.timer.start() def sigue_andando(self): return self.timer.isActive()
class SensorWorker(QObject): def __init__(self, parent=None, min_freq=20, max_freq=20000, sample_rate=500, name=""): super().__init__(parent) # worker self.sensor = MockSensor(min_freq, max_freq, sample_rate, name) # timer self.timer = QTimer() self.timer.setInterval(self.sensor.interval) # worker thread self.worker_thread = QThread() self.sensor.moveToThread(self.worker_thread) self.worker_thread.start() # signals self.timer.timeout.connect(self.sensor.generate_signal) def is_sensor_active(self): return self.timer.isActive() def start(self): self.sensor.generate_results_file() self.timer.start() def stop(self): self.timer.stop() self.sensor.stop()
class HistogramPlot(pyqtgraph.PlotWidget): def __init__(self, args): pyqtgraph.PlotWidget.__init__(self) self.args = args self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.length_warning) def data_changed(self, data, mods, title): try: y = data[self.args.y]["value"] if self.args.x is None: x = None else: x = data[self.args.x]["value"] except KeyError: return if x is None: x = list(range(len(y) + 1)) if len(y) and len(x) == len(y) + 1: self.timer.stop() self.clear() self.plot(x, y, stepMode=True, fillLevel=0, brush=(0, 0, 255, 150)) self.setTitle(title) else: if not self.timer.isActive(): self.timer.start(1000) def length_warning(self): self.clear() text = "⚠️ dataset lengths mismatch:\n"\ "There should be one more bin boundaries than there are Y values" self.addItem(pyqtgraph.TextItem(text))
class Demo(QWidget): def __init__(self): super(Demo, self).__init__() self.label = QLabel('0', self) # 1 self.label.setAlignment(Qt.AlignCenter) self.step = 0 # 2 self.timer = QTimer(self) # 3 self.timer.timeout.connect(self.update_func) self.ss_button = QPushButton('Start', self) # 4 self.ss_button.clicked.connect(self.start_stop_func) self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.label) self.v_layout.addWidget(self.ss_button) self.setLayout(self.v_layout) def start_stop_func(self): if not self.timer.isActive(): self.ss_button.setText('Stop') self.timer.start(100) else: self.ss_button.setText('Start') self.timer.stop() def update_func(self): self.step += 1 self.label.setText(str(self.step))
class ConsoleBuffer(QObject): excrete = pyqtSignal(str) def __init__(self, parent=None, minimum=0.050): super(ConsoleBuffer, self).__init__(parent) self.minimum = minimum self.last_time = time.monotonic() - minimum self.buffer = [] self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self._excrete) @pyqtSlot(str) def consume(self, s): self.buffer.append(s) delta = time.monotonic() - self.last_time remaining = self.minimum - delta if remaining <= 0: self._excrete() elif not self.timer.isActive(): self.timer.start(int(1000 * remaining)) def _excrete(self): self.timer.stop() s = ''.join(self.buffer) if len(s): self.last_time = time.monotonic() self.excrete.emit(s) self.buffer = []
class InputFilter(QLineEdit): def __init__(self, on_key_press): super(InputFilter, self).__init__() self.on_key_press = on_key_press self.last_text = '' self.typing = QTimer() self.typing.timeout.connect(self.notify_text_change) def notify_text_change(self): text = self.text().strip() if text != self.last_text: self.last_text = text self.on_key_press() def keyPressEvent(self, event): super(InputFilter, self).keyPressEvent(event) if self.typing.isActive(): return self.typing.start(2000) def get_text(self): return self.last_text def setText(self, p_str): super(InputFilter, self).setText(p_str) self.last_text = p_str
def test_import_csv(self): def accept_csv_dialog(): w = next((w for w in QApplication.topLevelWidgets() if isinstance(w, CSVImportDialog)), None) w.accept() timer.stop() timer = QTimer(self.form) timer.setSingleShot(True) timer.setInterval(10) timer.timeout.connect(accept_csv_dialog) self.assertEqual(self.form.signal_tab_controller.num_frames, 0) timer.start() self.form.add_files([self.get_path_for_filename("csvtest.csv")]) self.assertFalse(timer.isActive()) self.assertEqual( self.form.signal_tab_controller.signal_frames[0].signal. num_samples, 100) self.assertTrue( os.path.isfile(self.get_path_for_filename("csvtest.complex"))) timer.start() self.form.add_files([self.get_path_for_filename("csvtest.csv")]) self.assertEqual(self.form.signal_tab_controller.num_frames, 2) self.assertTrue( os.path.isfile(self.get_path_for_filename("csvtest_1.complex"))) os.remove(self.get_path_for_filename("csvtest.complex")) os.remove(self.get_path_for_filename("csvtest_1.complex"))
class QCursorGif: def initCursor(self, cursors, parent=None): # 记录默认的光标 self._oldCursor = Qt.ArrowCursor self.setOldCursor(parent) # 加载光标图片 self._cursorImages = [QCursor(QPixmap(cursor)) for cursor in cursors] self._cursorIndex = 0 self._cursorCount = len(self._cursorImages) - 1 # 创建刷新定时器 self._cursorTimeout = 200 self._cursorTimer = QTimer(parent) self._cursorTimer.timeout.connect(self._doBusy) def _doBusy(self): if self._cursorIndex > self._cursorCount: self._cursorIndex = 0 QApplication.instance().setOverrideCursor( self._cursorImages[self._cursorIndex]) self._cursorIndex += 1 def startBusy(self): if not self._cursorTimer.isActive(): self._cursorTimer.start(self._cursorTimeout) def stopBusy(self): self._cursorTimer.stop() QApplication.instance().setOverrideCursor(self._oldCursor) def setCursorTimeout(self, timeout): self._cursorTimeout = timeout def setOldCursor(self, parent=None): self._oldCursor = (parent.cursor() or Qt.ArrowCursor) if parent else ( QApplication.instance().overrideCursor() or Qt.ArrowCursor)
class MainWindow(QWidget): # class constructor def __init__(self): # call QWidget constructor super().__init__() self.ui = Ui_Form() self.ui.setupUi(self) self.frame = QFrame(self) self.frame.setFrameStyle(QFrame.Panel | QFrame.Raised) self.frame.setGeometry(150, 30, 100, 100) # create a timer self.timer = QTimer() # set timer timeout callback function self.timer.timeout.connect(self.viewCam) # set control_bt callback clicked function self.ui.control_bt.clicked.connect(self.controlTimer) self.ui.control_bt.clicked.connect(self.animation) # view camera def viewCam(self): # read image in BGR format ret, image = self.cap.read() # convert image to RGB format image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # get image infos height, width, channel = image.shape step = channel * width # create QImage from image qImg = QImage(image.data, width, height, step, QImage.Format_RGB888) # show image in img_label self.ui.image_label.setPixmap(QPixmap.fromImage(qImg)) # start/stop timer def controlTimer(self): # if timer is stopped if not self.timer.isActive(): # create video capture self.cap = cv2.VideoCapture(0) # start timer self.timer.start(20) # update control_bt text self.ui.control_bt.setText("Stop") # if timer is started else: # stop timer self.timer.stop() # release video capture self.cap.release() # update control_bt text self.ui.control_bt.setText("Start") def animation(self): self.anim = QPropertyAnimation(self.frame, b"geometry") self.anim.setDuration(1000) self.anim.setStartValue(QRect(0, 0, 100, 30)) self.anim.setEndValue(QRect(250, 250, 100, 30)) self.anim.start()
class MusicPosition(plugin.ViewSpacePlugin): def __init__(self, space): self._timer = QTimer(singleShot=True, timeout=self.slotTimeout) self._waittimer = QTimer(singleShot=True, timeout=self.slotTimeout) self._label = QLabel() space.status.layout().insertWidget(1, self._label) self._view = lambda: None space.viewChanged.connect(self.slotViewChanged) view = space.activeView() if view: self.slotViewChanged(view) def slotViewChanged(self, view): old = self._view() if old: self.disconnectView(old) self._view = weakref.ref(view) self.connectView(view) self.startTimer() def connectView(self, view): view.cursorPositionChanged.connect(self.startTimer) view.document().contentsChanged.connect(self.startWaitTimer) def disconnectView(self, view): view.cursorPositionChanged.disconnect(self.startTimer) view.document().contentsChanged.disconnect(self.startWaitTimer) def startWaitTimer(self): """Called when the document changes, waits longer to prevent stutter.""" self._waittimer.start(900) self._timer.stop() def startTimer(self): """Called when the cursor moves.""" if not self._waittimer.isActive(): self._timer.start(100) def slotTimeout(self): """Called when one of the timers fires.""" view = self._view() if view: d = view.document() c = view.textCursor() import documentinfo m = documentinfo.music(d) import ly.duration if c.hasSelection(): cursortools.strip_selection(c) length = m.time_length(c.selectionStart(), c.selectionEnd()) text = _("Length: {length}").format( length=ly.duration.format_fraction( length)) if length is not None else '' else: pos = m.time_position(c.position()) text = _("Pos: {pos}").format(pos=ly.duration.format_fraction( pos)) if pos is not None else '' self._label.setText(text) self._label.setVisible(bool(text))
class MainWindow(QWidget): def __init__(self): super().__init__() print("### [Space] to Start and Pause/Unpause ###") self.base_time = pc() self.OrderStimulus() self.initStimulus() self.initUI() self.show() info = StreamInfo('Oddballstimulus', 'stimulation', 1, 100, 'float32', 'oddballstimu20190531') self.outlet = StreamOutlet(info) def initUI(self): self.setWindowTitle("Oddball-Task Stimulus") self.timer = QTimer(self) self.timer.timeout.connect(self.update) #updateでpaintEventメソッドを更新 #割合が決まった刺激をランダムに並び替えたリストを作るメソッド def OrderStimulus(self): self.stimulusOrder = list() #作成するリスト sumOfTarget = int(sumOfStimulus * ratioOfTarget) for i in range(sumOfTarget): self.stimulusOrder.append(0) for i in range(sumOfStimulus - sumOfTarget): self.stimulusOrder.append(1) random.shuffle(self.stimulusOrder) #要素をシャッフル # 刺激を定義するメソッド def initStimulus(self): self.stim = Stimulus(self.stimulusOrder) #キーが押される度に実行されるメソッド def keyPressEvent(self, e): if e.key() == Qt.Key_Space: if (self.stim.counterStimulus >= sumOfStimulus): #終了するときのキー操作 self.timer.start() sys.exit() elif self.timer.isActive(): #実行途中で止めるときのキー操作 self.timer.stop() else: #実行途中で再開するときのキー操作 self.stim.resetTimer() self.timer.start(frameRate) #frameRateは,いらないかも #updateされるたびに実行されるメソッド def paintEvent(self, QPaintEvent): curr_time = pc() if (self.stim.counterStimulus >= sumOfStimulus): #stim.~でclass Stimulus側(1つのインスタンス)の値を持ってこれる print("### [Space] to Exit from This App ###") self.timer.stop() else: self.stim.draw(curr_time) stimu = [int(self.stim.on)] self.outlet.push_sample(stimu)
class MusicPosition(plugin.ViewSpacePlugin): def __init__(self, space): self._timer = QTimer(singleShot=True, timeout=self.slotTimeout) self._waittimer = QTimer(singleShot=True, timeout=self.slotTimeout) self._label = QLabel() space.status.layout().insertWidget(1, self._label) self._view = lambda: None space.viewChanged.connect(self.slotViewChanged) view = space.activeView() if view: self.slotViewChanged(view) def slotViewChanged(self, view): old = self._view() if old: self.disconnectView(old) self._view = weakref.ref(view) self.connectView(view) self.startTimer() def connectView(self, view): view.cursorPositionChanged.connect(self.startTimer) view.document().contentsChanged.connect(self.startWaitTimer) def disconnectView(self, view): view.cursorPositionChanged.disconnect(self.startTimer) view.document().contentsChanged.disconnect(self.startWaitTimer) def startWaitTimer(self): """Called when the document changes, waits longer to prevent stutter.""" self._waittimer.start(900) self._timer.stop() def startTimer(self): """Called when the cursor moves.""" if not self._waittimer.isActive(): self._timer.start(100) def slotTimeout(self): """Called when one of the timers fires.""" view = self._view() if view: d = view.document() c = view.textCursor() import documentinfo m = documentinfo.music(d) import ly.duration if c.hasSelection(): cursortools.strip_selection(c) length = m.time_length(c.selectionStart(), c.selectionEnd()) text = _("Length: {length}").format( length=ly.duration.format_fraction(length)) if length is not None else '' else: pos = m.time_position(c.position()) text = _("Pos: {pos}").format( pos=ly.duration.format_fraction(pos)) if pos is not None else '' self._label.setText(text) self._label.setVisible(bool(text))
class ToolTip(QWidget): """ 气泡的父级窗口 """ def __init__(self, text: str = '', parent=None): super().__init__(parent) # 实例化气泡子窗口 self.subToolTip = SubToolTip(text, parent=self) # 实例化布局和定时器 self.all_h_layout = QHBoxLayout() self.timer = QTimer(self) # 初始化部件和布局 self.initWidget() self.initLayout() def initWidget(self): """ 初始化小部件 """ # 设置自己为无边框窗口 self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window) # 设置背景透明和鼠标穿透 self.setAttribute(Qt.WA_TranslucentBackground) self.setStyleSheet('background:transparent') self.setAutoFillBackground(False) self.timer.setInterval(5000) self.timer.timeout.connect(self.timeoutEvent) # 引用子窗口的setText成员函数 self.setText = self.subToolTip.setText self.isWordWrap = self.subToolTip.isWordWrap def initLayout(self): """ 初始化布局 """ self.all_h_layout.addWidget(self.subToolTip, 0, Qt.AlignCenter) self.all_h_layout.setContentsMargins(40,40,40,40) # 根据布局的内小部件大小调整窗体大小 self.all_h_layout.setSizeConstraint(QHBoxLayout.SetFixedSize) self.setLayout(self.all_h_layout) def timeoutEvent(self): """ 定时器溢出时隐藏提示条 """ self.hide() self.timer.stop() def show(self): """ 显示提示条并打开定时器 """ # 窗口置顶 self.raise_() self.timer.start() super().show() def hide(self): """ 隐藏提示条并停止定时器 """ if not self.timer.isActive(): self.timer.stop() super().hide() def leaveEvent(self, e): # 由于自己会捕获leaveEvent,所以如果自己leaveEvent被触发说明兄弟部件的leaveEvent也被触发了 self.hide()
class ShortMessage(ShortMessageUI): def __init__(self, *args, **kwargs): super(ShortMessage, self).__init__(*args, **kwargs) self.animation_timer = QTimer(self) self.animation_timer.timeout.connect(self.refresh_animation_text) # 默认添加今日的内容控件 current_datetime = datetime.today().strftime("%Y-%m-%dT00:00:00") content_widget = ContentWidget(current_datetime, timer_start=True) self.animation_timer.start(600) self.scroll_area.setWidget(content_widget) self.date_edit.dateChanged.connect(self.current_date_changed) def refresh_animation_text(self): """ 资讯持续更新中 """ tips = self.animation_text.text() tip_points = tips.split(' ')[1] if len(tip_points) > 5: self.animation_text.setText("资讯持续更新中 ") else: self.animation_text.setText("资讯持续更新中 " + "·" * (len(tip_points) + 1)) def current_date_changed(self, date): """ 当前时间发生改变 """ date_edit_text = self.date_edit.text() current_date = datetime.strptime(date_edit_text, "%Y-%m-%d") current_date_str = current_date.strftime("%Y-%m-%dT00:00:00") week_name = self.WEEKS.get(current_date.strftime("%w")) self.current_date.setText(date_edit_text + week_name) if current_date_str == datetime.today().strftime("%Y-%m-%dT00:00:00"): timer_start = True self.animation_text.show() if not self.animation_timer.isActive(): self.animation_timer.start(600) else: timer_start = False self.animation_text.hide() if self.animation_timer.isActive(): self.animation_timer.stop() self.animation_text.setText("资讯持续更新中 ") content_widget = ContentWidget(current_date_str, timer_start=timer_start) self.scroll_area.setWidget(content_widget)
class TigToolTip(QLabel): """Custom QToolTip type widget based on a QLabel for plot information output.""" def __init__(self): QLabel.__init__(self) self.installEventFilter(self) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_QuitOnClose) self.setStyleSheet("QLabel {background-color: white; color: black;}") self.setTextFormat(Qt.RichText) self.setWordWrap(True) self._qtimer = QTimer() self._qtimer.timeout.connect(self.hideText) def showText(self, location, text, timeout=7000): if self._qtimer.isActive(): self._qtimer.start(timeout) self.setText(text) text_size = self.fontMetrics().boundingRect(self.text()) # TODO - find a better way for the below sizes if text_size.width() > 900: max_w = 700 max_h = text_size.height() * 4 else: max_w = 900 max_h = text_size.height() self.setGeometry(location.x(), location.y(), max_w, max_h) self.show() self._qtimer.start(timeout) def hideText(self): self._qtimer.stop() self.close() # available on Ubuntu by default # disabling for now issue #163 # os.system('notify-send "Tigger" "Information copied to clipboard"') def eventFilter(self, source, event): # event.type() 25 == QEvent.WindowDeactivate. # In this context, TigToolTip is the top most window and when application has been changed in terms of state, # for example to another application, the TigToolTip needs to be closed, otherwise it will remain on the screen. if event.type() == QEvent.WindowDeactivate: if self._qtimer.isActive(): self._qtimer.stop() self.close() return super(TigToolTip, self).eventFilter(source, event)
class RandomTimer(QObject): timeout = Signal() intervalChanged = Signal() activeChanged = Signal() def __init__(self, parent=None): super(RandomTimer, self).__init__() self.timer = QTimer() self.timer.timeout.connect(self.timeout) @Slot() def start(self): print("timer start") if not self.timer.isActive(): self.timer.start() self.activeChanged.emit() @Slot() def stop(self): print("timer stop") if self.timer.isActive(): self.timer.stop() self.activeChanged.emit() @Slot(int, int, result=int) def randomInterval(self, min, max): range = max - min msec = min + random.randint(0, range) return msec @pyqtProperty(int, notify=intervalChanged) def interval(self): return self.timer.interval() @interval.setter def interval(self, msec): if self.timer.interval() != msec: self.timer.setInterval(msec) self.intervalChanged.emit() print("interval = {}".format(self.timer.interval())) @pyqtProperty(bool, notify=activeChanged) def active(self): return self.timer.isActive()
class GameBoard(QWidget): def __init__(self): super().__init__() self.width = 1000 self.height = 1000 self.resize(self.width, self.height) self.background = Background((self.width, self.height)) self.scan_threads = [] self.image = Image.new("RGB", (1000, 1000)) self.drawer = ImageDraw.Draw(self.image) self.meeples = [] self.label = QLabel(self) self.timer = QTimer() self.run() def random_point(self): return random.randint(0, self.width), random.randint(0, self.height) def add_meeple(self, position=None, color=None): if not color: color = random.choice(["green", "blue", "yellow", "red"]) if not position: position = self.random_point() self.meeples.append(Meeple(self, position, color)) def draw(self): self.image.paste(self.background.image) for meeple in self.meeples: meeple.draw() self.label.setPixmap(pil2pixmap(self.image)) def update(self): for meeple in self.meeples: meeple.update() self.draw() def run(self): self.draw() self.timer.start(100) self.timer.timeout.connect(self.update) def pause(self): if self.timer.isActive(): self.timer.stop() else: self.timer.start() def keyPressEvent(self, event): if event.key() == Qt.Key_Space: self.pause() if event.key() == Qt.Key_Escape: self.close() event.accept()
class App(QMainWindow): def __init__(self): super().__init__() self.timer = QTimer() uic.loadUi(r"EDIZ\Kamera.ui", self) # graphical user interface self.initUI() def initUI(self): self.btAc.clicked.connect(self.KameraAc) self.btKapat.setEnabled(False) self.btKapat.clicked.connect(self.KameraKapat) self.show() def KameraAc(self): if not self.timer.isActive(): self.cam = cv2.VideoCapture(0, cv2.CAP_DSHOW) self.timer.start(3) self.btAc.setEnabled(False) self.btKapat.setEnabled(True) self.Goster() else: self.cam.release() self.timer.stop() def Goster(self): while True: ret, frame = self.cam.read() buyukFaktor = 0.6 frame = cv2.resize(frame, None, fx=buyukFaktor, fy=buyukFaktor, interpolation=cv2.INTER_AREA) height, width, channel = frame.shape # (640,480,3) step = channel * width # (3*480) #----------------- qImg = QImage( frame.data, width, height, step, QImage.Format_BGR888) # farklı kanallardan aldığı veriyi # bir resim olarak ekrana yansıtmak üzere QImage kullanıldı self.lblCam.setPixmap(QPixmap.fromImage( qImg)) # Alınan resmi label içerisinde gösterebilmek için # QPixmap kullanıldı if cv2.waitKey(1) & 0xFF == ord("q"): break self.cam.release() self.timer.stop() def KameraKapat(self): try: self.cam.release() except: pass self.timer.stop() self.close()
class MiTimers(QObject): senal_actualizar_tiempo = pyqtSignal(dict) def __init__(self): super().__init__() self.segundos = 0 self.minutos = 0 self.horas = 0 self.timer = QTimer() self.timer.setInterval(500) self.timer.timeout.connect(self.enviar_dato) def enviar_dato(self): self.segundos += 1 if self.segundos < 60: self.senal_actualizar_tiempo.emit({ "seg": self.segundos, "min": self.minutos, "hora": self.horas }) elif self.segundos >= 60: self.minutos += 1 self.segundos = 0 self.senal_actualizar_tiempo.emit({ "seg": self.segundos, "min": self.minutos, "hora": self.horas }) elif self.minutos >= 60: self.minutos = 0 self.segundos = 0 self.horas += 1 self.senal_actualizar_tiempo.emit({ "seg": self.segundos, "min": self.minutos, "hora": self.horas }) def comenzar(self): self.timer.start() def parar(self): self.timer.stop() def reiniciar(self): self.segundos = 0 self.minutos = 0 self.horas = 0 def isActive(self): if self.timer.isActive() == True: return True else: return False
class OperatorViewWindow(QDialog): def __init__(self, *args, **kwargs): super(OperatorViewWindow, self).__init__(*args) warning = kwargs.get('warn', [0,0,0]) failure = kwargs.get('fail', [0,0,0]) tail = kwargs.get('tail', 10) remove_old = kwargs.get('remove_old', False) descriptions = kwargs.get('descriptions', [None] * 3) self.timer = None self.ui = form_class() self.ui.setupUi(self) self.status_bar = QStatusBar(self) self.ui.windowLayout.addWidget(self.status_bar) self.engine = kwargs['callback'] self.graphs = [DynamicRiskCanvas(self, coordinate=i + 1, warning=warning[i], failure=failure[i], tail=tail, remove_old=remove_old, description=descriptions[i]) for i in range(3)] for graph in self.graphs: self.ui.y_layout.addWidget(graph) def initial_graphics_fill(self, real_values, predicted_values, risk_values, time_ticks): for i, graph in enumerate(self.graphs): graph.compute_initial_figure(real_values.T[i], predicted_values[i], risk_values[i], time_ticks) def update_graphics(self, real_value, predicted_values, risk_values, forecast_ticks): for i, graph in enumerate(self.graphs): graph.update_figure(real_value[i], predicted_values[i], risk_values[i], forecast_ticks) def closeEvent(self, event): if self.timer and self.timer.isActive(): self.timer.stop() self.timer.disconnect() self.timer.deleteLater() super(QDialog, self).closeEvent(event) @pyqtSlot() def manipulate_timer(self): if not self.timer: self.ui.start_button.setText('Pause') self.timer = QTimer(self) self.timer.timeout.connect(self.execute_iteration) self.timer.start(50) elif self.timer.isActive(): self.ui.start_button.setText('Continue') self.timer.stop() else: self.ui.start_button.setText('Pause') self.timer.start() @pyqtSlot() def execute_iteration(self): self.engine.launch()
class VariableManager(plugin.DocumentPlugin): """Caches variables in the document and monitors for changes. The changed() Signal is emitted some time after the list of variables has been changed. It is recommended to not change the document itself in response to this signal. """ changed = signals.Signal() # without argument def __init__(self, doc): self._updateTimer = QTimer(singleShot=True, timeout=self.slotTimeout) self._variables = self.readVariables() if doc.__class__ == document.EditorDocument: doc.contentsChange.connect(self.slotContentsChange) doc.closed.connect(self._updateTimer.stop) # just to be sure def slotTimeout(self): variables = self.readVariables() if variables != self._variables: self._variables = variables self.changed() def slotContentsChange(self, position, removed, added): """Called if the document changes.""" if (self.document().findBlock(position).blockNumber() < _LINES or self.document().findBlock(position + added).blockNumber() > self.document().blockCount() - _LINES): self._updateTimer.start(500) def variables(self): """Returns the document variables (cached) as a dictionary. This method is recommended.""" if self._updateTimer.isActive(): # an update is pending, force it self._updateTimer.stop() self.slotTimeout() return self._variables def readVariables(self): """Reads the variables from the document and returns a dictionary. Internal.""" count = self.document().blockCount() blocks = [self.document().firstBlock()] if count > _LINES * 2: blocks.append(self.document().findBlockByNumber(count - _LINES)) count = _LINES def lines(block): for i in range(count): yield block.text() block = block.next() variables = {} for block in blocks: variables.update(m.group(1, 2) for n, m in positions(lines(block))) return variables
class Timer(QLCDNumber): textChanged = pyqtSignal(str) started = pyqtSignal() stopped = pyqtSignal() reset_ = pyqtSignal() def __init__(self, parent=None): super().__init__(parent) self.timer = QTimer() self.timer.timeout.connect(self.tick) self.reset() def start(self): self.timer.start(1000) self.started.emit() def stop(self): self.timer.stop() self.stopped.emit() def reset(self): self.time = QTime(0, 0) self.display(self.time.toString()) self.reset_.emit() def isRunning(self): return self.timer.isActive() def text(self): if self.time.hour() == 0: return self.time.toString('mm:ss') else: return self.time.toString('h:mm:ss') def seconds(self): return self.time.hour() * 3600 + self.time.minute() * 60 + self.time.second() def tick(self): self.time = self.time.addSecs(1) text = self.text() if len(text) != self.digitCount(): self.setDigitCount(len(text)) self.display(text) self.textChanged.emit(text)
class GitterClient(QObject): """Manage a connection to Gitter """ def __init__(self, manager, auth): super().__init__() self._manager = manager self._auth = auth self._rooms = None self._net = QNetworkAccessManager() self._rooms = None self._user = None self._refresh_timer = None connected = pyqtSignal() disconnected = pyqtSignal() def connect(self): if self._refresh_timer is None: self._refresh_timer = QTimer() self._refresh_timer.timeout.connect(self.refresh_client) if not self._refresh_timer.isActive(): self._rooms = Rooms(self._net, self._auth, self._manager) self._rooms.ready.connect(self.rooms_initialized) self._refresh_timer.start(600000) def refresh_client(self): # really should check to see if changed? self._rooms.load() def rooms_initialized(self): logger.debug("rooms initialized") self.connected.emit() self._rooms.ready.disconnect(self.rooms_initialized) def disconnect(self): self._refresh_timer.stop() self._rooms.disconnect() @property def rooms(self): return self._rooms
class GlobalTimer: """All parsing and highlighting is done in main loop thread. If parsing is being done for long time, main loop gets blocked. Therefore SyntaxHighlighter controls, how long parsign is going, and, if too long, schedules timer and releases main loop. One global timer is used by all Qutepart instances, because main loop time usage must not depend on opened files count """ def __init__(self): self._timer = QTimer(QApplication.instance()) self._timer.setSingleShot(True) self._timer.timeout.connect(self._onTimer) self._scheduledCallbacks = [] def isActive(self): return self._timer.isActive() def scheduleCallback(self, callback): if not callback in self._scheduledCallbacks: self._scheduledCallbacks.append(callback) self._timer.start() def unScheduleCallback(self, callback): if callback in self._scheduledCallbacks: self._scheduledCallbacks.remove(callback) if not self._scheduledCallbacks: self._timer.stop() def isCallbackScheduled(self, callback): return callback in self._scheduledCallbacks def _onTimer(self): if self._scheduledCallbacks: callback = self._scheduledCallbacks.pop() callback() if self._scheduledCallbacks: self._timer.start()
class PomodoroControl(QObject): """A class that controls pomodoro working segments. Warning: May drift because no absolute times are used. """ pomodoro_begin = pyqtSignal() pomodoro_complete = pyqtSignal() time_update = pyqtSignal([float, float]) # signal: number of seconds elapsed, number of seconds remaining def __init__(self, parent=None): super(PomodoroControl, self).__init__(parent) self.timer = QTimer(self) self.timer.timeout.connect(self.handle_second) self.seconds_remaining = 0 self.seconds_elapsed = 0 @property def is_running(self): return self.timer.isActive() def start(self, seconds_remaining): self.seconds_remaining = seconds_remaining self.seconds_elapsed = 0 self.timer.start(1000) self.pomodoro_begin.emit() self.time_update.emit(self.seconds_elapsed, self.seconds_remaining) def early_finish(self): self.timer.stop() self.pomodoro_complete.emit() def handle_second(self): self.seconds_elapsed += 1 self.seconds_remaining -= 1 self.time_update.emit(self.seconds_elapsed, self.seconds_remaining) if self.seconds_remaining == 0: self.timer.stop() self.pomodoro_complete.emit()
class ZhaoChaFrame(QWidget): game_hwnd = 0 # 游戏的窗体句柄 bgpixmap = None pixmap = None my_visible = False # GAME_CLASS = "#32770" # GAME_TITLE = "大家来找茬" GAME_CLASS = "MozillaWindowClass" GAME_TITLE = "游戏全屏 - Mozilla Firefox" WIDTH = 500 # 大图宽 HEIGHT = 450 # 大图高 ANCHOR_LEFT_X = 8 # 左图X起点 ANCHOR_RIGHT_X = 517 # 右图X起点 ANCHOR_Y = 190 # Y起点 CLIP_WIDTH = 10 CLIP_HEIGHT = 10 DIFF_LIMIT = 2000 # 差异阀值,两片图形对比差异差异超过此值视为不一样 # 查找区域 # 大图版 1024 x 738 BIG_WIDTH = 498 # 大图宽 BIG_HEIGHT = 448 # 大图高 BIG_ANCHOR_LEFT_X = 8 # 左图X起点 BIG_ANCHOR_RIGHT_X = 517 # 右图X起点 BIG_ANCHOR_Y = 190 # Y起点 BIG_CLIP_WIDTH = 10 BIG_CLIP_HEIGHT = 10 BIG_DIFF_LIMIT = 2000 # 差异阀值,两片图形对比差异差异超过此值视为不一样 # 小图版 800 x 600 SMALL_WIDTH = 381 # 大图宽 SMALL_HEIGHT = 286 # 大图高 SMALL_ANCHOR_LEFT_X = 10 # 左图X起点 SMALL_ANCHOR_RIGHT_X = 403 # 右图X起点 SMALL_ANCHOR_Y = 184 # Y起点 SMALL_CLIP_WIDTH = 10 SMALL_CLIP_HEIGHT = 10 SMALL_DIFF_LIMIT = 2000 # 差异阀值,两片图形对比差异差异超过此值视为不一样 # 存储对比结果 二位数组,映射每一个基块 result = [] clock = 0 def __init__(self, parent=None): QWidget.__init__(self) # QWidget.__init__(self, parent, flags=Qt.FramelessWindowHint | Qt.Window | Qt.WindowStaysOnTopHint) # 设置背景透明,这样按钮不会太难看 # self.setAttribute(Qt.WA_TranslucentBackground, True) # 这些属性让程序不在任务栏出现标题 # self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Popup | Qt.Tool) # 托盘 self.icon = QIcon(":\icon.png") self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setIcon(self.icon) self.trayIcon.setToolTip(u"QQ找茬助手") self.trayIcon.show() self.trayIcon.showMessage(u"QQ找茬助手", u"QQ找茬助手已经待命,进入游戏即可激活") self.action = QAction(u"退出QQ找茬助手", self, triggered=sys.exit) self.menu = QMenu(self) self.menu.addAction(self.action) self.trayIcon.setContextMenu(self.menu) # 定时探测游戏 self.stick_timer = QTimer() self.stick_timer.start(20) # self.connect(self.stick_timer, SIGNAL('timeout()'), self.StickTarget) # 这个QLabel其实就是中间绘图区的背景 self.label = QLabel(self) self.pixmap = QPixmap(self.size()) # 刷新按钮 self.btn_compare = QPushButton(self) self.btn_compare.setText(u"对比") # self.connect(self.btn_compare, SIGNAL('clicked()'), self.Compare) # 开关 self.btn_toggle = QPushButton(self) self.btn_toggle.setText(u"擦除") # self.connect(self.btn_toggle, SIGNAL('clicked()'), self.Clear) self.HideMe() def StickTarget(self): '''让本窗体粘附在目标窗体上''' # 找到目标窗口句柄 game_hwnd = win32gui.FindWindow(self.GAME_CLASS, self.GAME_TITLE) if game_hwnd == 0: if self.my_visible: # 如果游戏窗体不可见,比如最小化、关闭了,隐藏自己 self.HideMe() return else: self.game_hwnd = game_hwnd try: window_rect = win32gui.GetWindowRect(self.game_hwnd) if self.game_hwnd == win32gui.GetForegroundWindow() and window_rect[0] > 0: point = QPoint(window_rect[0], window_rect[1]) size = QSize(window_rect[2] - window_rect[0], window_rect[3] - window_rect[1]) if self.size() != size: self.SyncSize(size) if self.pos() != point: self.move(point) if not self.my_visible: self.ShowMe() # self.FindAndShow() elif win32gui.GetForegroundWindow() != int(self.winId()) and self.my_visible: # 游戏窗口隐藏时,同时隐藏找碴助手 self.HideMe() except: if self.my_visible: self.HideMe() def paintEvent(self, event): if not self.my_visible: self.move(-2000, -2000) self.pixmap.fill() p = QPainter(self.pixmap) p.setPen(QPen(QBrush(QColor(0, 0, 0)), 2)) for row in range(len(self.result)): for col in range(len(self.result[0])): if self.result[row][col] != 0: # 定一个基点,避免算数太难看 base_l_x = self.ANCHOR_LEFT_X + self.CLIP_WIDTH * col base_r_x = self.ANCHOR_RIGHT_X + self.CLIP_WIDTH * col base_y = self.ANCHOR_Y + self.CLIP_HEIGHT * row if row == 0 or self.result[row - 1][col] == 0: # 如果是第一行,或者上面的格子为空,画一条上边 p.drawLine(base_l_x, base_y, base_l_x + self.CLIP_WIDTH, base_y) p.drawLine(base_r_x, base_y, base_r_x + self.CLIP_WIDTH, base_y) if row == len(self.result) - 1 or self.result[row + 1][col] == 0: # 如果是最后一行,或者下面的格子为空,画一条下边 p.drawLine(base_l_x, base_y + self.CLIP_HEIGHT, base_l_x + self.CLIP_WIDTH, base_y + self.CLIP_HEIGHT) p.drawLine(base_r_x, base_y + self.CLIP_HEIGHT, base_r_x + self.CLIP_WIDTH, base_y + self.CLIP_HEIGHT) if col == 0 or self.result[row][col - 1] == 0: # 如果是第一列,或者左边的格子为空,画一条左边 p.drawLine(base_l_x, base_y, base_l_x, base_y + self.CLIP_HEIGHT) p.drawLine(base_r_x, base_y, base_r_x, base_y + self.CLIP_HEIGHT) if col == len(self.result[0]) - 1 or self.result[row][col + 1] == 0: # 如果是第一列,或者右边的格子为空,画一条右边 p.drawLine(base_l_x + self.CLIP_WIDTH, base_y, base_l_x + self.CLIP_WIDTH, base_y + self.CLIP_HEIGHT) p.drawLine(base_r_x + self.CLIP_WIDTH, base_y, base_r_x + self.CLIP_WIDTH, base_y + self.CLIP_HEIGHT) p.fillRect(self.btn_compare.geometry(), QBrush(QColor(0, 0, 0))) p.fillRect(self.btn_toggle.geometry(), QBrush(QColor(0, 0, 0))) self.setMask(QBitmap(self.pixmap)) def Clear(self): self.ResetResult() self.repaint() def ShowMe(self): self.my_visible = True self.repaint() def HideMe(self): self.my_visible = False self.repaint() def Compare(self): # 对比 if self.stick_timer.isActive(): self.FindAndShow() else: self.stick_timer.start() def ResetResult(self): # 清楚之前计算的结果 self.result = [[0 for a in range(0, self.WIDTH / self.CLIP_WIDTH)] for b in range(0, self.HEIGHT / self.CLIP_HEIGHT)] def SyncSize(self, size): self.resize(size) if self.width() == 1024 and self.height() == 738: self.WIDTH = self.BIG_WIDTH self.HEIGHT = self.BIG_HEIGHT self.ANCHOR_LEFT_X = self.BIG_ANCHOR_LEFT_X self.ANCHOR_RIGHT_X = self.BIG_ANCHOR_RIGHT_X self.ANCHOR_Y = self.BIG_ANCHOR_Y self.CLIP_WIDTH = self.BIG_CLIP_WIDTH self.CLIP_HEIGHT = self.BIG_CLIP_HEIGHT self.DIFF_LIMIT = self.BIG_DIFF_LIMIT self.btn_compare.setGeometry(611, 650, 100, 40) self.btn_toggle.setGeometry(715, 650, 100, 40) elif self.width() == 800 and self.height() == 600: self.WIDTH = self.SMALL_WIDTH self.HEIGHT = self.SMALL_HEIGHT self.ANCHOR_LEFT_X = self.SMALL_ANCHOR_LEFT_X self.ANCHOR_RIGHT_X = self.SMALL_ANCHOR_RIGHT_X self.ANCHOR_Y = self.SMALL_ANCHOR_Y self.CLIP_WIDTH = self.SMALL_CLIP_WIDTH self.CLIP_HEIGHT = self.SMALL_CLIP_HEIGHT self.DIFF_LIMIT = self.SMALL_DIFF_LIMIT self.btn_compare.setGeometry(472, 496, 100, 40) self.btn_toggle.setGeometry(576, 496, 100, 40) else: print("游戏窗体大小匹配错误") return self.pixmap = QPixmap(self.size()) self.bgpixmap = QPixmap(self.width(), self.HEIGHT) self.bgpixmap.fill(QColor(0, 0, 255)) self.label.setGeometry(0, self.ANCHOR_Y, self.width(), self.HEIGHT) self.label.setPixmap(self.bgpixmap) def FindAndShow(self): # 截取游戏窗口内容 self.my_visible = True self.DebugTime("init") ## 裁剪得到左右的内容图片 win32gui.ShowWindow(self.game_hwnd, win32con.SW_RESTORE) # 强行显示界面后才好截图 win32gui.SetForegroundWindow(self.game_hwnd) # 将游戏窗口提到最前 src_image = ImageGrab.grab((self.x(), self.y() + self.ANCHOR_Y, self.x() + self.ANCHOR_RIGHT_X + self.WIDTH, self.y() + self.ANCHOR_Y + self.HEIGHT)) left_box = (self.ANCHOR_LEFT_X, 0, self.ANCHOR_LEFT_X + self.WIDTH, self.HEIGHT) right_box = (self.ANCHOR_RIGHT_X, 0, self.ANCHOR_RIGHT_X + self.WIDTH, self.HEIGHT) image_left = src_image.crop(left_box) image_right = src_image.crop(right_box) # image_left.show() # image_right.show() self.DebugTime("拆图完成") # 将左右大图裁剪成多个小图分别进行对比 self.ResetResult() for col in range(0, self.WIDTH / self.CLIP_WIDTH): for row in range(0, self.HEIGHT / self.CLIP_HEIGHT): clip_box = (col * self.CLIP_WIDTH, row * self.CLIP_HEIGHT, (col + 1) * self.CLIP_WIDTH, (row + 1) * self.CLIP_HEIGHT) clip_image_left = image_left.crop(clip_box) clip_image_right = image_right.crop(clip_box) clip_diff = self.compare(clip_image_left, clip_image_right) if sum(clip_diff) > self.DIFF_LIMIT: self.result[row][col] = 1 self.DebugTime("对比") self.repaint() self.DebugTime("绘制") # print "----------------------" # for i in range(len(self.result)): # Y轴循环 #for j in range(len(self.result[i])): # X轴循环 #print self.result[i][j], #print #print "----------------------" def compare(self, image_a, image_b): '''返回两图的差异值 返回两图红绿蓝差值万分比之和''' histogram_a = image_a.histogram() histogram_b = image_b.histogram() if len(histogram_a) != 768 or len(histogram_b) != 768: return None red_a = 0 red_b = 0 for i in range(0, 256): red_a += histogram_a[i + 0] * i red_b += histogram_b[i + 0] * i diff_red = 0 if red_a + red_b > 0: diff_red = abs(red_a - red_b) * 10000 / max(red_a, red_b) green_a = 0 green_b = 0 for i in range(0, 256): green_a += histogram_a[i + 256] * i green_b += histogram_b[i + 256] * i diff_green = 0 if green_a + green_b > 0: diff_green = abs(green_a - green_b) * 10000 / max(green_a, green_b) blue_a = 0 blue_b = 0 for i in range(0, 256): blue_a += histogram_a[i + 512] * i blue_b += histogram_b[i + 512] * i diff_blue = 0 if blue_a + blue_b > 0: diff_blue = abs(blue_a - blue_b) * 10000 / max(blue_a, blue_b) return diff_red, diff_green, diff_blue def DebugTime(self, text=""): return if self.clock > 0: print time.clock() - self.clock, text self.clock = time.clock()
class PrinterOutputDevice(QObject, OutputDevice): def __init__(self, device_id, parent = None): super().__init__(device_id = device_id, parent = parent) self._container_registry = ContainerRegistry.getInstance() self._target_bed_temperature = 0 self._bed_temperature = 0 self._num_extruders = 1 self._hotend_temperatures = [0] * self._num_extruders self._target_hotend_temperatures = [0] * self._num_extruders self._material_ids = [""] * self._num_extruders self._hotend_ids = [""] * self._num_extruders self._progress = 0 self._head_x = 0 self._head_y = 0 self._head_z = 0 self._connection_state = ConnectionState.closed self._connection_text = "" self._time_elapsed = 0 self._time_total = 0 self._job_state = "" self._job_name = "" self._error_text = "" self._accepts_commands = True self._preheat_bed_timeout = 900 # Default time-out for pre-heating the bed, in seconds. self._preheat_bed_timer = QTimer() # Timer that tracks how long to preheat still. self._preheat_bed_timer.setSingleShot(True) self._preheat_bed_timer.timeout.connect(self.cancelPreheatBed) self._printer_state = "" self._printer_type = "unknown" self._camera_active = False self._monitor_view_qml_path = "" self._monitor_component = None self._monitor_item = None self._qml_context = None def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None): raise NotImplementedError("requestWrite needs to be implemented") ## Signals # Signal to be emitted when bed temp is changed bedTemperatureChanged = pyqtSignal() # Signal to be emitted when target bed temp is changed targetBedTemperatureChanged = pyqtSignal() # Signal when the progress is changed (usually when this output device is printing / sending lots of data) progressChanged = pyqtSignal() # Signal to be emitted when hotend temp is changed hotendTemperaturesChanged = pyqtSignal() # Signal to be emitted when target hotend temp is changed targetHotendTemperaturesChanged = pyqtSignal() # Signal to be emitted when head position is changed (x,y,z) headPositionChanged = pyqtSignal() # Signal to be emitted when either of the material ids is changed materialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal to be emitted when either of the hotend ids is changed hotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal that is emitted every time connection state is changed. # it also sends it's own device_id (for convenience sake) connectionStateChanged = pyqtSignal(str) connectionTextChanged = pyqtSignal() timeElapsedChanged = pyqtSignal() timeTotalChanged = pyqtSignal() jobStateChanged = pyqtSignal() jobNameChanged = pyqtSignal() errorTextChanged = pyqtSignal() acceptsCommandsChanged = pyqtSignal() printerStateChanged = pyqtSignal() printerTypeChanged = pyqtSignal() # Signal to be emitted when some drastic change occurs in the remaining time (not when the time just passes on normally). preheatBedRemainingTimeChanged = pyqtSignal() @pyqtProperty(QObject, constant=True) def monitorItem(self): # Note that we specifically only check if the monitor component is created. # It could be that it failed to actually create the qml item! If we check if the item was created, it will try to # create the item (and fail) every time. if not self._monitor_component: self._createMonitorViewFromQML() return self._monitor_item def _createMonitorViewFromQML(self): path = QUrl.fromLocalFile(self._monitor_view_qml_path) # Because of garbage collection we need to keep this referenced by python. self._monitor_component = QQmlComponent(Application.getInstance()._engine, path) # Check if the context was already requested before (Printer output device might have multiple items in the future) if self._qml_context is None: self._qml_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("OutputDevice", self) self._monitor_item = self._monitor_component.create(self._qml_context) if self._monitor_item is None: Logger.log("e", "QQmlComponent status %s", self._monitor_component.status()) Logger.log("e", "QQmlComponent error string %s", self._monitor_component.errorString()) @pyqtProperty(str, notify=printerTypeChanged) def printerType(self): return self._printer_type @pyqtProperty(str, notify=printerStateChanged) def printerState(self): return self._printer_state @pyqtProperty(str, notify = jobStateChanged) def jobState(self): return self._job_state def _updatePrinterType(self, printer_type): if self._printer_type != printer_type: self._printer_type = printer_type self.printerTypeChanged.emit() def _updatePrinterState(self, printer_state): if self._printer_state != printer_state: self._printer_state = printer_state self.printerStateChanged.emit() def _updateJobState(self, job_state): if self._job_state != job_state: self._job_state = job_state self.jobStateChanged.emit() @pyqtSlot(str) def setJobState(self, job_state): self._setJobState(job_state) def _setJobState(self, job_state): Logger.log("w", "_setJobState is not implemented by this output device") @pyqtSlot() def startCamera(self): self._camera_active = True self._startCamera() def _startCamera(self): Logger.log("w", "_startCamera is not implemented by this output device") @pyqtSlot() def stopCamera(self): self._camera_active = False self._stopCamera() def _stopCamera(self): Logger.log("w", "_stopCamera is not implemented by this output device") @pyqtProperty(str, notify = jobNameChanged) def jobName(self): return self._job_name def setJobName(self, name): if self._job_name != name: self._job_name = name self.jobNameChanged.emit() ## Gives a human-readable address where the device can be found. @pyqtProperty(str, constant = True) def address(self): Logger.log("w", "address is not implemented by this output device.") ## A human-readable name for the device. @pyqtProperty(str, constant = True) def name(self): Logger.log("w", "name is not implemented by this output device.") return "" @pyqtProperty(str, notify = errorTextChanged) def errorText(self): return self._error_text ## Set the error-text that is shown in the print monitor in case of an error def setErrorText(self, error_text): if self._error_text != error_text: self._error_text = error_text self.errorTextChanged.emit() @pyqtProperty(bool, notify = acceptsCommandsChanged) def acceptsCommands(self): return self._accepts_commands ## Set a flag to signal the UI that the printer is not (yet) ready to receive commands def setAcceptsCommands(self, accepts_commands): if self._accepts_commands != accepts_commands: self._accepts_commands = accepts_commands self.acceptsCommandsChanged.emit() ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) # /sa _getBedTemperature implementation function @pyqtProperty(float, notify = bedTemperatureChanged) def bedTemperature(self): return self._bed_temperature ## Set the (target) bed temperature # This function is "final" (do not re-implement) # /param temperature new target temperature of the bed (in deg C) # /sa _setTargetBedTemperature implementation function @pyqtSlot(int) def setTargetBedTemperature(self, temperature): self._setTargetBedTemperature(temperature) if self._target_bed_temperature != temperature: self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() ## The total duration of the time-out to pre-heat the bed, in seconds. # # \return The duration of the time-out to pre-heat the bed, in seconds. @pyqtProperty(int, constant = True) def preheatBedTimeout(self): return self._preheat_bed_timeout ## The remaining duration of the pre-heating of the bed. # # This is formatted in M:SS format. # \return The duration of the time-out to pre-heat the bed, formatted. @pyqtProperty(str, notify = preheatBedRemainingTimeChanged) def preheatBedRemainingTime(self): if not self._preheat_bed_timer.isActive(): return "" period = self._preheat_bed_timer.remainingTime() if period <= 0: return "" minutes, period = divmod(period, 60000) #60000 milliseconds in a minute. seconds, _ = divmod(period, 1000) #1000 milliseconds in a second. if minutes <= 0 and seconds <= 0: return "" return "%d:%02d" % (minutes, seconds) ## Time the print has been printing. # Note that timeTotal - timeElapsed should give time remaining. @pyqtProperty(float, notify = timeElapsedChanged) def timeElapsed(self): return self._time_elapsed ## Total time of the print # Note that timeTotal - timeElapsed should give time remaining. @pyqtProperty(float, notify=timeTotalChanged) def timeTotal(self): return self._time_total @pyqtSlot(float) def setTimeTotal(self, new_total): if self._time_total != new_total: self._time_total = new_total self.timeTotalChanged.emit() @pyqtSlot(float) def setTimeElapsed(self, time_elapsed): if self._time_elapsed != time_elapsed: self._time_elapsed = time_elapsed self.timeElapsedChanged.emit() ## Home the head of the connected printer # This function is "final" (do not re-implement) # /sa _homeHead implementation function @pyqtSlot() def homeHead(self): self._homeHead() ## Home the head of the connected printer # This is an implementation function and should be overriden by children. def _homeHead(self): Logger.log("w", "_homeHead is not implemented by this output device") ## Home the bed of the connected printer # This function is "final" (do not re-implement) # /sa _homeBed implementation function @pyqtSlot() def homeBed(self): self._homeBed() ## Home the bed of the connected printer # This is an implementation function and should be overriden by children. # /sa homeBed def _homeBed(self): Logger.log("w", "_homeBed is not implemented by this output device") ## Protected setter for the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) # /sa setTargetBedTemperature def _setTargetBedTemperature(self, temperature): Logger.log("w", "_setTargetBedTemperature is not implemented by this output device") ## Pre-heats the heated bed of the printer. # # \param temperature The temperature to heat the bed to, in degrees # Celsius. # \param duration How long the bed should stay warm, in seconds. @pyqtSlot(float, float) def preheatBed(self, temperature, duration): Logger.log("w", "preheatBed is not implemented by this output device.") ## Cancels pre-heating the heated bed of the printer. # # If the bed is not pre-heated, nothing happens. @pyqtSlot() def cancelPreheatBed(self): Logger.log("w", "cancelPreheatBed is not implemented by this output device.") ## Protected setter for the current bed temperature. # This simply sets the bed temperature, but ensures that a signal is emitted. # /param temperature temperature of the bed. def _setBedTemperature(self, temperature): if self._bed_temperature != temperature: self._bed_temperature = temperature self.bedTemperatureChanged.emit() ## Get the target bed temperature if connected printer (if any) @pyqtProperty(int, notify = targetBedTemperatureChanged) def targetBedTemperature(self): return self._target_bed_temperature ## Set the (target) hotend temperature # This function is "final" (do not re-implement) # /param index the index of the hotend that needs to change temperature # /param temperature The temperature it needs to change to (in deg celsius). # /sa _setTargetHotendTemperature implementation function @pyqtSlot(int, int) def setTargetHotendTemperature(self, index, temperature): self._setTargetHotendTemperature(index, temperature) if self._target_hotend_temperatures[index] != temperature: self._target_hotend_temperatures[index] = temperature self.targetHotendTemperaturesChanged.emit() ## Implementation function of setTargetHotendTemperature. # /param index Index of the hotend to set the temperature of # /param temperature Temperature to set the hotend to (in deg C) # /sa setTargetHotendTemperature def _setTargetHotendTemperature(self, index, temperature): Logger.log("w", "_setTargetHotendTemperature is not implemented by this output device") @pyqtProperty("QVariantList", notify = targetHotendTemperaturesChanged) def targetHotendTemperatures(self): return self._target_hotend_temperatures @pyqtProperty("QVariantList", notify = hotendTemperaturesChanged) def hotendTemperatures(self): return self._hotend_temperatures ## Protected setter for the current hotend temperature. # This simply sets the hotend temperature, but ensures that a signal is emitted. # /param index Index of the hotend # /param temperature temperature of the hotend (in deg C) def _setHotendTemperature(self, index, temperature): if self._hotend_temperatures[index] != temperature: self._hotend_temperatures[index] = temperature self.hotendTemperaturesChanged.emit() @pyqtProperty("QVariantList", notify = materialIdChanged) def materialIds(self): return self._material_ids @pyqtProperty("QVariantList", notify = materialIdChanged) def materialNames(self): result = [] for material_id in self._material_ids: if material_id is None: result.append(i18n_catalog.i18nc("@item:material", "No material loaded")) continue containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) if containers: result.append(containers[0].getName()) else: result.append(i18n_catalog.i18nc("@item:material", "Unknown material")) return result ## List of the colours of the currently loaded materials. # # The list is in order of extruders. If there is no material in an # extruder, the colour is shown as transparent. # # The colours are returned in hex-format AARRGGBB or RRGGBB # (e.g. #800000ff for transparent blue or #00ff00 for pure green). @pyqtProperty("QVariantList", notify = materialIdChanged) def materialColors(self): result = [] for material_id in self._material_ids: if material_id is None: result.append("#00000000") #No material. continue containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) if containers: result.append(containers[0].getMetaDataEntry("color_code")) else: result.append("#00000000") #Unknown material. return result ## Protected setter for the current material id. # /param index Index of the extruder # /param material_id id of the material def _setMaterialId(self, index, material_id): if material_id and material_id != "" and material_id != self._material_ids[index]: Logger.log("d", "Setting material id of hotend %d to %s" % (index, material_id)) self._material_ids[index] = material_id self.materialIdChanged.emit(index, material_id) @pyqtProperty("QVariantList", notify = hotendIdChanged) def hotendIds(self): return self._hotend_ids ## Protected setter for the current hotend id. # /param index Index of the extruder # /param hotend_id id of the hotend def _setHotendId(self, index, hotend_id): if hotend_id and hotend_id != self._hotend_ids[index]: Logger.log("d", "Setting hotend id of hotend %d to %s" % (index, hotend_id)) self._hotend_ids[index] = hotend_id self.hotendIdChanged.emit(index, hotend_id) elif not hotend_id: Logger.log("d", "Removing hotend id of hotend %d.", index) self._hotend_ids[index] = None self.hotendIdChanged.emit(index, None) ## Let the user decide if the hotends and/or material should be synced with the printer # NB: the UX needs to be implemented by the plugin def materialHotendChangedMessage(self, callback): Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'") callback(QMessageBox.Yes) ## Attempt to establish connection def connect(self): raise NotImplementedError("connect needs to be implemented") ## Attempt to close the connection def close(self): raise NotImplementedError("close needs to be implemented") @pyqtProperty(bool, notify = connectionStateChanged) def connectionState(self): return self._connection_state ## Set the connection state of this output device. # /param connection_state ConnectionState enum. def setConnectionState(self, connection_state): if self._connection_state != connection_state: self._connection_state = connection_state self.connectionStateChanged.emit(self._id) @pyqtProperty(str, notify = connectionTextChanged) def connectionText(self): return self._connection_text ## Set a text that is shown on top of the print monitor tab def setConnectionText(self, connection_text): if self._connection_text != connection_text: self._connection_text = connection_text self.connectionTextChanged.emit() ## Ensure that close gets called when object is destroyed def __del__(self): self.close() ## Get the x position of the head. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headX(self): return self._head_x ## Get the y position of the head. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headY(self): return self._head_y ## Get the z position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headZ(self): return self._head_z ## Update the saved position of the head # This function should be called when a new position for the head is received. def _updateHeadPosition(self, x, y ,z): position_changed = False if self._head_x != x: self._head_x = x position_changed = True if self._head_y != y: self._head_y = y position_changed = True if self._head_z != z: self._head_z = z position_changed = True if position_changed: self.headPositionChanged.emit() ## Set the position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) # /param x new x location of the head. # /param y new y location of the head. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadPosition implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def setHeadPosition(self, x, y, z, speed = 3000): self._setHeadPosition(x, y , z, speed) ## Set the X position of the head. # This function is "final" (do not re-implement) # /param x x position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadx implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadX(self, x, speed = 3000): self._setHeadX(x, speed) ## Set the Y position of the head. # This function is "final" (do not re-implement) # /param y y position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadY implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadY(self, y, speed = 3000): self._setHeadY(y, speed) ## Set the Z position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) # /param z z position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadZ implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadZ(self, z, speed = 3000): self._setHeadY(z, speed) ## Move the head of the printer. # Note that this is a relative move. If you want to move the head to a specific position you can use # setHeadPosition # This function is "final" (do not re-implement) # /param x distance in x to move # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) # /sa _moveHead implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def moveHead(self, x = 0, y = 0, z = 0, speed = 3000): self._moveHead(x, y, z, speed) ## Implementation function of moveHead. # /param x distance in x to move # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) # /sa moveHead def _moveHead(self, x, y, z, speed): Logger.log("w", "_moveHead is not implemented by this output device") ## Implementation function of setHeadPosition. # /param x new x location of the head. # /param y new y location of the head. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa setHeadPosition def _setHeadPosition(self, x, y, z, speed): Logger.log("w", "_setHeadPosition is not implemented by this output device") ## Implementation function of setHeadX. # /param x new x location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa setHeadX def _setHeadX(self, x, speed): Logger.log("w", "_setHeadX is not implemented by this output device") ## Implementation function of setHeadY. # /param y new y location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadY def _setHeadY(self, y, speed): Logger.log("w", "_setHeadY is not implemented by this output device") ## Implementation function of setHeadZ. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadZ def _setHeadZ(self, z, speed): Logger.log("w", "_setHeadZ is not implemented by this output device") ## Get the progress of any currently active process. # This function is "final" (do not re-implement) # /sa _getProgress # /returns float progress of the process. -1 indicates that there is no process. @pyqtProperty(float, notify = progressChanged) def progress(self): return self._progress ## Set the progress of any currently active process # /param progress Progress of the process. def setProgress(self, progress): if self._progress != progress: self._progress = progress self.progressChanged.emit()
class Widget(QWidget, ScreenWidget): name = "timeSetup" def __init__(self): QWidget.__init__(self) self.ui = Ui_DateTimeWidget() self.ui.setupUi(self) self.timer = QTimer(self) self.from_time_updater = True self.is_date_changed = False self.current_zone = "" self.tz_dict = {} self.continents = [] self.countries = [] for country, data in yali.localedata.locales.items(): if country == ctx.consts.lang: if data.has_key("timezone"): ctx.installData.timezone = data["timezone"] # Append continents and countries the time zone dictionary self.createTZDictionary() # Sort continent list self.sortContinents() # Append sorted continents to combobox self.loadContinents() # Load current continents country list self.getCountries(self.current_zone["continent"]) # Highlight the current zone self.index = self.ui.continentList.findText(self.current_zone["continent"]) self.ui.continentList.setCurrentIndex(self.index) self.index = self.ui.countryList.findText(self.current_zone["country"]) self.ui.countryList.setCurrentIndex(self.index) # Initialize widget signal and slots self.__initSignals__() self.ui.calendarWidget.setDate(QDate.currentDate()) self.pthread = None self.pds_messagebox = PMessageBox(self) self.pds_messagebox.enableOverlay() self.timer.start(1000) def __initSignals__(self): self.ui.timeEdit.timeChanged[QTime].connect(self.timerStop) self.ui.calendarWidget.dateChanged.connect(self.dateChanged) self.timer.timeout.connect(self.updateClock) self.ui.continentList.activated[str].connect(self.getCountries) def createTZDictionary(self): tz = TimeZoneList() zones = [ x.timeZone for x in tz.getEntries() ] zones.sort() for zone in zones: split = zone.split("/") # Human readable continent names continent_pretty_name = split[0].replace("_", " ") continent_pretty_name = continent_pretty_name # Some country names can be like Argentina/Catamarca so this fixes the splitting problem # caused by zone.split("/") # # Remove continent info and take the rest as the country name split.pop(0) country_pretty_name = " / ".join(split) # Human readable country names country_pretty_name = country_pretty_name.replace("_", " ") # Get current zone if zone == ctx.installData.timezone: self.current_zone = { "continent":continent_pretty_name, "country":country_pretty_name} # Append to dictionary if self.tz_dict.has_key(continent_pretty_name): self.tz_dict[continent_pretty_name].append([country_pretty_name, zone]) else: self.tz_dict[continent_pretty_name] = [[country_pretty_name, zone]] def sortContinents(self): for continent in self.tz_dict.keys(): self.continents.append(continent) self.continents.sort() def loadContinents(self): for continent in self.continents: self.ui.continentList.addItem(continent) def getCountries(self, continent): # Countries of the selected continent countries = self.tz_dict[str(continent)] self.ui.countryList.clear() for country, zone in countries: self.ui.countryList.addItem(country, zone) self.countries.append(country) def dateChanged(self): self.is_date_changed = True def timerStop(self): if self.from_time_updater: return # Human action detected; stop the timer. self.timer.stop() def updateClock(self): # What time is it ? cur = QTime.currentTime() self.from_time_updater = True self.ui.timeEdit.setTime(cur) self.from_time_updater = False def shown(self): self.timer.start(1000) if ctx.flags.install_type == ctx.STEP_BASE: self.pthread = PThread(self, self.startInit, self.dummy) def dummy(self): pass def setTime(self): ctx.interface.informationWindow.update(_("Adjusting time settings")) date = self.ui.calendarWidget.date() time = self.ui.timeEdit.time() args = "%02d%02d%02d%02d%04d.%02d" % (date.month(), date.day(), time.hour(), time.minute(), date.year(), time.second()) # Set current date and time ctx.logger.debug("Date/Time setting to %s" % args) yali.util.run_batch("date", [args]) # Sync date time with hardware ctx.logger.debug("YALI's time is syncing with the system.") yali.util.run_batch("hwclock", ["--systohc"]) ctx.interface.informationWindow.hide() def execute(self): if not self.timer.isActive() or self.is_date_changed: QTimer.singleShot(500, self.setTime) self.timer.stop() index = self.ui.countryList.currentIndex() ctx.installData.timezone = self.ui.countryList.itemData(index) ctx.logger.debug("Time zone selected as %s " % ctx.installData.timezone) if ctx.flags.install_type == ctx.STEP_BASE: #FIXME:Refactor hacky code ctx.installData.rootPassword = ctx.consts.default_password ctx.installData.hostName = yali.util.product_release() if ctx.storageInitialized: disks = filter(lambda d: not d.format.hidden, ctx.storage.disks) if len(disks) == 1: ctx.storage.clearPartDisks = [disks[0].name] ctx.mainScreen.step_increment = 2 else: ctx.mainScreen.step_increment = 1 return True else: self.pds_messagebox.setMessage(_("Storage Devices initialising...")) self.pds_messagebox.animate(start=MIDCENTER, stop=MIDCENTER) ctx.mainScreen.step_increment = 0 self.pthread.start() QTimer.singleShot(2, self.startStorageInitialize) return False return True def startInit(self): self.pds_messagebox.animate(start=MIDCENTER, stop=MIDCENTER) def startStorageInitialize(self): ctx.storageInitialized = yali.storage.initialize(ctx.storage, ctx.interface) self.initFinished() def initFinished(self): self.pds_messagebox.animate(start=CURRENT, stop=CURRENT, direction=OUT) disks = filter(lambda d: not d.format.hidden, ctx.storage.disks) if ctx.storageInitialized: if len(disks) == 1: ctx.storage.clearPartDisks = [disks[0].name] ctx.mainScreen.step_increment = 2 else: ctx.mainScreen.step_increment = 1 ctx.mainScreen.slotNext(dry_run=True) else: ctx.mainScreen.enableBack()
class MainWindow(QMainWindow): """Main window class.""" def __init__(self, parent=None): """Init class.""" super(MainWindow, self).__init__() self.setWindowTitle(__doc__.strip().capitalize()) self.statusBar().showMessage(" Choose one App and move the sliders !") self.setMinimumSize(480, 240) self.setMaximumSize(640, 2048) self.setWindowIcon(QIcon.fromTheme("preferences-system")) self.center() QShortcut("Ctrl+q", self, activated=lambda: self.close()) self.menuBar().addMenu("&File").addAction("Exit", exit) windowMenu = self.menuBar().addMenu("&Window") windowMenu.addAction("Minimize", lambda: self.showMinimized()) windowMenu.addAction("Maximize", lambda: self.showMaximized()) windowMenu.addAction("Restore", lambda: self.showNormal()) windowMenu.addAction("FullScreen", lambda: self.showFullScreen()) windowMenu.addAction("Center", lambda: self.center()) windowMenu.addAction("Top-Left", lambda: self.move(0, 0)) windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position()) windowMenu.addSeparator() windowMenu.addAction( "Increase size", lambda: self.resize(self.size().width() * 1.4, self.size().height() * 1.4)) windowMenu.addAction("Decrease size", lambda: self.resize( self.size().width() // 1.4, self.size().height() // 1.4)) windowMenu.addAction("Minimum size", lambda: self.resize(self.minimumSize())) windowMenu.addAction("Maximum size", lambda: self.resize(self.maximumSize())) windowMenu.addAction("Horizontal Wide", lambda: self.resize( self.maximumSize().width(), self.minimumSize().height())) windowMenu.addAction("Vertical Tall", lambda: self.resize( self.minimumSize().width(), self.maximumSize().height())) windowMenu.addSeparator() windowMenu.addAction("Disable Resize", lambda: self.setFixedSize(self.size())) windowMenu.addAction("Set Interface Font...", lambda: self.setFont(QFontDialog.getFont()[0])) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("About Qt 5", lambda: QMessageBox.aboutQt(self)) helpMenu.addAction("About Python 3", lambda: open_new_tab('https://www.python.org')) helpMenu.addAction("About" + __doc__, lambda: QMessageBox.about(self, __doc__, HELP)) helpMenu.addSeparator() helpMenu.addAction( "Keyboard Shortcut", lambda: QMessageBox.information(self, __doc__, "<b>Quit = CTRL+Q")) helpMenu.addAction("View Source Code", lambda: call('xdg-open ' + __file__, shell=True)) helpMenu.addAction("View GitHub Repo", lambda: open_new_tab(__url__)) helpMenu.addAction("Report Bugs", lambda: open_new_tab( 'https://github.com/juancarlospaco/pyority/issues?state=open')) helpMenu.addAction("Check Updates", lambda: Downloader(self)) container, child_container = QWidget(), QWidget() container_layout = QVBoxLayout(container) child_layout = QHBoxLayout(child_container) self.setCentralWidget(container) # widgets group0 = QGroupBox("My Apps") group1, group2 = QGroupBox("CPU Priority"), QGroupBox("HDD Priority") child_layout.addWidget(group0) child_layout.addWidget(group1) child_layout.addWidget(group2) container_layout.addWidget(child_container) # table self.table = QTableWidget() self.table.setColumnCount(1) self.table.verticalHeader().setVisible(True) self.table.horizontalHeader().setVisible(False) self.table.setShowGrid(False) self.table.setAlternatingRowColors(True) self.table.setIconSize(QSize(64, 64)) self.table.setSelectionMode(QAbstractItemView.SingleSelection) self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) # Graphic effect glow = QGraphicsDropShadowEffect(self) glow.setOffset(0) glow.setBlurRadius(9) glow.setColor(QColor(99, 255, 255)) self.table.setGraphicsEffect(glow) glow.setEnabled(True) processes = self.generate_process_list() self.table.setRowCount(len(processes)) for index, process in enumerate(processes): item = QTableWidgetItem( QIcon.fromTheme(process.name().split()[0].split('/')[0]), process.name().split()[0].split('/')[0].strip()) item.setData(Qt.UserRole, process) item.setToolTip("{}, {}, {}, {}".format( process.name(), process.nice(), process.ionice()[1], process.pid)) self.table.setItem(index, 0, item) self.table.clicked.connect(lambda: self.sliderhdd.setDisabled(False)) self.table.clicked.connect(lambda: self.slidercpu.setDisabled(False)) self.table.clicked.connect(lambda: self.slidercpu.setValue( int(tuple(self.table.currentItem().toolTip().split(","))[1]))) self.table.clicked.connect(lambda: self.sliderhdd.setValue( int(tuple(self.table.currentItem().toolTip().split(","))[2]))) self.table.resizeColumnsToContents() # self.table.resizeRowsToContents() # sliders self.slidercpu = QSlider() self.slidercpu.setRange(0, 19) self.slidercpu.setSingleStep(1) self.slidercpu.setTickPosition(3) self.slidercpu.setDisabled(True) self.slidercpu.setInvertedAppearance(True) self.slidercpu.setInvertedControls(True) self.slidercpu.valueChanged.connect(self.set_cpu_value) self.slidercpu.valueChanged.connect( lambda: self.slidercpu.setToolTip(str(self.slidercpu.value()))) # Timer to start self.slidercpu_timer = QTimer(self) self.slidercpu_timer.setSingleShot(True) self.slidercpu_timer.timeout.connect(self.on_slidercpu_timer_timeout) QLabel(self.slidercpu).setPixmap( QIcon.fromTheme("list-add").pixmap(16)) QVBoxLayout(group1).addWidget(self.slidercpu) self.sliderhdd = QSlider() self.sliderhdd.setRange(0, 7) self.sliderhdd.setSingleStep(1) self.sliderhdd.setTickPosition(3) self.sliderhdd.setDisabled(True) self.sliderhdd.setInvertedAppearance(True) self.sliderhdd.setInvertedControls(True) self.sliderhdd.valueChanged.connect(self.set_hdd_value) self.sliderhdd.valueChanged.connect( lambda: self.sliderhdd.setToolTip(str(self.sliderhdd.value()))) # Timer to start self.sliderhdd_timer = QTimer(self) self.sliderhdd_timer.setSingleShot(True) self.sliderhdd_timer.timeout.connect(self.on_sliderhdd_timer_timeout) QLabel(self.sliderhdd).setPixmap( QIcon.fromTheme("list-add").pixmap(16)) QVBoxLayout(group2).addWidget(self.sliderhdd) QVBoxLayout(group0).addWidget(self.table) def set_cpu_value(self): """Set the CPU value.""" if self.slidercpu_timer.isActive(): self.slidercpu_timer.stop() self.slidercpu_timer.start(1000) def set_hdd_value(self): """Set the Disk value.""" if self.sliderhdd_timer.isActive(): self.sliderhdd_timer.stop() self.sliderhdd_timer.start(1000) def on_slidercpu_timer_timeout(self): """What to do on slider timer time out.""" pid = int(tuple(self.table.currentItem().toolTip().split(","))[3]) psutil.Process(pid).nice(self.slidercpu.value()) nice_result = 'Nice before: {},nice after: {}.'.format( psutil.Process(pid).nice(), psutil.Process(pid).nice()) self.statusBar().showMessage(nice_result, 5000) log.info(nice_result) def on_sliderhdd_timer_timeout(self): """What to do on slider timer time out.""" pid = int(tuple(self.table.currentItem().toolTip().split(","))[3]) ionice_before = psutil.Process(pid).ionice() psutil.Process(pid).ionice( 2 if self.sliderhdd.value() < 7 else 0, self.sliderhdd.value() if self.sliderhdd.value() < 7 else None) nice_result = 'ionice before: {}, ionice after: {}'.format( ionice_before, psutil.Process(pid).ionice()) self.statusBar().showMessage(nice_result, 5000) log.info(nice_result) def center(self): """Center Window on the Current Screen,with Multi-Monitor support.""" window_geometry = self.frameGeometry() mousepointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mousepointer_position) centerPoint = QApplication.desktop().screenGeometry(screen).center() window_geometry.moveCenter(centerPoint) self.move(window_geometry.topLeft()) def move_to_mouse_position(self): """Center the Window on the Current Mouse position.""" window_geometry = self.frameGeometry() window_geometry.moveCenter(QApplication.desktop().cursor().pos()) self.move(window_geometry.topLeft()) def closeEvent(self, event): """Ask to Quit.""" the_conditional_is_true = QMessageBox.question( self, __doc__.title(), 'Quit ?.', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes event.accept() if the_conditional_is_true else event.ignore() def generate_process_list(self): """Return a list of processes.""" return [p for p in psutil.process_iter() if p.username() == getuser()]
class TrickPanel(QWidget): """Widget for presenting trick""" _CARD_RECTS = ( QRectF( _IMAGE_WIDTH + _MARGIN, _IMAGE_HEIGHT + _MARGIN, _IMAGE_WIDTH, _IMAGE_HEIGHT), QRectF( 0, _IMAGE_HEIGHT / 2 + _MARGIN, _IMAGE_WIDTH, _IMAGE_HEIGHT), QRectF( _IMAGE_WIDTH + _MARGIN, 0, _IMAGE_WIDTH, _IMAGE_HEIGHT), QRectF( 2 * _IMAGE_WIDTH + 2 * _MARGIN, _IMAGE_HEIGHT / 2 + _MARGIN, _IMAGE_WIDTH, _IMAGE_HEIGHT), ) _SIZE = QSize( _CARD_RECTS[3].x() + _IMAGE_WIDTH + 1, _CARD_RECTS[0].y() + _IMAGE_HEIGHT + 1) def __init__(self, parent=None): """Initialize trick panel Keyword Arguments: parent -- the parent widget """ super().__init__(parent) self.setMinimumSize(self._SIZE) self._rect_map = {} self._cards = [] self._timer = QTimer(self) self._timer.setSingleShot(True) self._timer.setInterval(2000) self._timer.timeout.connect(self._clear_cards) def setPlayerPosition(self, position): """Set position of the current player The player must be set before setting cards in the trick. The card played by the current player is laid at the bottom of the panel. """ self._rect_map = dict(zip(positions.rotate(position), self._CARD_RECTS)) def setCards(self, cards): """Set cards in the trick This method accepts as argument an array consisting of position card pairs. The positions and cards may be either in the serialized or the internal representation. See the bridge protocol specification. If the argument is empty (i.e. clearing the trick), the cards are removed after a delay to give the players an opportunity to see the last card played to the trick. """ if not self._rect_map: return def _generate_position_card_pair(pair): return ( positions.asPosition(pair[POSITION_TAG]), asCard(pair[CARD_TAG])) try: new_cards = [_generate_position_card_pair(pair) for pair in cards] except Exception: raise messaging.ProtocolError("Invalid cards: %r" % cards) if new_cards: self._cards = new_cards self._timer.stop() self.repaint() elif self._cards: self._timer.start() def playCard(self, position, card): """Play single card to the trick This method puts card to given position, given that the position is empty or the timer period for clearing cards is ongoing. The arguments can be in internal or serialized representation. Keyword Arguments: position -- the position of the player who plays the card card -- the card played """ if self._timer.isActive(): del self._cards[:] position = positions.asPosition(position) if position not in (p for (p, _) in self._cards): card = asCard(card) self._cards.append((position, card)) self._timer.stop() self.repaint() def cards(self): """Return list containing the positions and cards played to the trick The list has tuples which have positions and as first the cards played by the player in the position as second elements. Both are in the internal representation. """ return list(self._cards) def paintEvent(self, event): """Paint cards""" painter = QPainter() painter.begin(self) for (position, card) in self._cards: _draw_image(painter, self._rect_map[position], CARD_IMAGES[card]) def _clear_cards(self): del self._cards[:] self.repaint()
class QVTKRenderWindowInteractor(QWidget): """ A QVTKRenderWindowInteractor for Python and Qt. Uses a vtkGenericRenderWindowInteractor to handle the interactions. Use GetRenderWindow() to get the vtkRenderWindow. Create with the keyword stereo=1 in order to generate a stereo-capable window. The user interface is summarized in vtkInteractorStyle.h: - Keypress j / Keypress t: toggle between joystick (position sensitive) and trackball (motion sensitive) styles. In joystick style, motion occurs continuously as long as a mouse button is pressed. In trackball style, motion occurs when the mouse button is pressed and the mouse pointer moves. - Keypress c / Keypress o: toggle between camera and object (actor) modes. In camera mode, mouse events affect the camera position and focal point. In object mode, mouse events affect the actor that is under the mouse pointer. - Button 1: rotate the camera around its focal point (if camera mode) or rotate the actor around its origin (if actor mode). The rotation is in the direction defined from the center of the renderer's viewport towards the mouse position. In joystick mode, the magnitude of the rotation is determined by the distance the mouse is from the center of the render window. - Button 2: pan the camera (if camera mode) or translate the actor (if object mode). In joystick mode, the direction of pan or translation is from the center of the viewport towards the mouse position. In trackball mode, the direction of motion is the direction the mouse moves. (Note: with 2-button mice, pan is defined as <Shift>-Button 1.) - Button 3: zoom the camera (if camera mode) or scale the actor (if object mode). Zoom in/increase scale if the mouse position is in the top half of the viewport; zoom out/decrease scale if the mouse position is in the bottom half. In joystick mode, the amount of zoom is controlled by the distance of the mouse pointer from the horizontal centerline of the window. - Keypress 3: toggle the render window into and out of stereo mode. By default, red-blue stereo pairs are created. Some systems support Crystal Eyes LCD stereo glasses; you have to invoke SetStereoTypeToCrystalEyes() on the rendering window. Note: to use stereo you also need to pass a stereo=1 keyword argument to the constructor. - Keypress e: exit the application. - Keypress f: fly to the picked point - Keypress p: perform a pick operation. The render window interactor has an internal instance of vtkCellPicker that it uses to pick. - Keypress r: reset the camera view along the current view direction. Centers the actors and moves the camera so that all actors are visible. - Keypress s: modify the representation of all actors so that they are surfaces. - Keypress u: invoke the user-defined function. Typically, this keypress will bring up an interactor that you can type commands in. - Keypress w: modify the representation of all actors so that they are wireframe. """ # Map between VTK and Qt cursors. _CURSOR_MAP = { 0: Qt.ArrowCursor, # VTK_CURSOR_DEFAULT 1: Qt.ArrowCursor, # VTK_CURSOR_ARROW 2: Qt.SizeBDiagCursor, # VTK_CURSOR_SIZENE 3: Qt.SizeFDiagCursor, # VTK_CURSOR_SIZENWSE 4: Qt.SizeBDiagCursor, # VTK_CURSOR_SIZESW 5: Qt.SizeFDiagCursor, # VTK_CURSOR_SIZESE 6: Qt.SizeVerCursor, # VTK_CURSOR_SIZENS 7: Qt.SizeHorCursor, # VTK_CURSOR_SIZEWE 8: Qt.SizeAllCursor, # VTK_CURSOR_SIZEALL 9: Qt.PointingHandCursor, # VTK_CURSOR_HAND 10: Qt.CrossCursor, # VTK_CURSOR_CROSSHAIR } def __init__(self, parent=None, wflags=Qt.WindowFlags(), **kw): # the current button self._ActiveButton = Qt.NoButton # private attributes self.__oldFocus = None self.__saveX = 0 self.__saveY = 0 self.__saveModifiers = Qt.NoModifier self.__saveButtons = Qt.NoButton # do special handling of some keywords: # stereo, rw try: stereo = bool(kw['stereo']) except KeyError: stereo = False try: rw = kw['rw'] except KeyError: rw = None # create qt-level widget QWidget.__init__(self, parent, wflags|Qt.MSWindowsOwnDC) if rw: # user-supplied render window self._RenderWindow = rw else: self._RenderWindow = vtk.vtkRenderWindow() wid = self._get_win_id() self._RenderWindow.SetWindowInfo(wid) self._should_set_parent_info = (sys.platform == 'win32') if stereo: # stereo mode self._RenderWindow.StereoCapableWindowOn() self._RenderWindow.SetStereoTypeToCrystalEyes() try: self._Iren = kw['iren'] except KeyError: self._Iren = vtk.vtkGenericRenderWindowInteractor() self._Iren.SetRenderWindow(self._RenderWindow) # do all the necessary qt setup self.setAttribute(Qt.WA_OpaquePaintEvent) self.setAttribute(Qt.WA_PaintOnScreen) self.setMouseTracking(True) # get all mouse events self.setFocusPolicy(Qt.WheelFocus) self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self._Timer = QTimer(self) self._Timer.timeout.connect(self.TimerEvent) # add wheel timer to fix scrolling issue with trackpad self.wheel_timer = QTimer() self.wheel_timer.setSingleShot(True) self.wheel_timer.setInterval(25) self.wheel_timer.timeout.connect(self._emit_wheel_event) self.wheel_accumulator = 0 self._saved_wheel_event_info = () self._Iren.AddObserver('CreateTimerEvent', messenger.send) messenger.connect(self._Iren, 'CreateTimerEvent', self.CreateTimer) self._Iren.AddObserver('DestroyTimerEvent', messenger.send) messenger.connect(self._Iren, 'DestroyTimerEvent', self.DestroyTimer) self._RenderWindow.AddObserver('CursorChangedEvent', messenger.send) messenger.connect(self._RenderWindow, 'CursorChangedEvent', self.CursorChangedEvent) # Create a hidden child widget and connect its destroyed signal to its # parent ``Finalize`` slot. The hidden children will be destroyed # before its parent thus allowing cleanup of VTK elements. self._hidden = QWidget(self) self._hidden.hide() self._hidden.destroyed.connect(self.Finalize) def __getattr__(self, attr): """Makes the object behave like a vtkGenericRenderWindowInteractor""" if attr == '__vtk__': return lambda t=self._Iren: t elif hasattr(self._Iren, attr): return getattr(self._Iren, attr) else: raise AttributeError( self.__class__.__name__ + " has no attribute named " + attr ) def _get_win_id(self): WId = self.winId() # Python2 if type(WId).__name__ == 'PyCObject': from ctypes import pythonapi, c_void_p, py_object pythonapi.PyCObject_AsVoidPtr.restype = c_void_p pythonapi.PyCObject_AsVoidPtr.argtypes = [py_object] WId = pythonapi.PyCObject_AsVoidPtr(WId) # Python3 elif type(WId).__name__ == 'PyCapsule': from ctypes import pythonapi, c_void_p, py_object, c_char_p pythonapi.PyCapsule_GetName.restype = c_char_p pythonapi.PyCapsule_GetName.argtypes = [py_object] name = pythonapi.PyCapsule_GetName(WId) pythonapi.PyCapsule_GetPointer.restype = c_void_p pythonapi.PyCapsule_GetPointer.argtypes = [py_object, c_char_p] WId = pythonapi.PyCapsule_GetPointer(WId, name) return str(int(WId)) def Finalize(self): ''' Call internal cleanup method on VTK objects ''' self._RenderWindow.Finalize() def CreateTimer(self, obj, evt): self._Timer.start(10) def DestroyTimer(self, obj, evt): self._Timer.stop() return 1 def TimerEvent(self): self._Iren.TimerEvent() def CursorChangedEvent(self, obj, evt): """Called when the CursorChangedEvent fires on the render window.""" # This indirection is needed since when the event fires, the current # cursor is not yet set so we defer this by which time the current # cursor should have been set. QTimer.singleShot(0, self.ShowCursor) def HideCursor(self): """Hides the cursor.""" self.setCursor(Qt.BlankCursor) def ShowCursor(self): """Shows the cursor.""" vtk_cursor = self._Iren.GetRenderWindow().GetCurrentCursor() qt_cursor = self._CURSOR_MAP.get(vtk_cursor, Qt.ArrowCursor) self.setCursor(qt_cursor) def closeEvent(self, evt): self.Finalize() def sizeHint(self): return QSize(400, 400) def paintEngine(self): return None def paintEvent(self, ev): self._RenderWindow.Render() def resizeEvent(self, ev): if self._should_set_parent_info: # Set the window info and parent info on every resize. # vtkWin32OpenGLRenderWindow will render using incorrect offsets if # the parent info is not given to it because it assumes that it # needs to make room for the title bar. winid = self._get_win_id() self._RenderWindow.SetWindowInfo(winid) parent = self.parent() if parent is not None: self._RenderWindow.SetParentInfo(winid) else: self._RenderWindow.SetParentInfo('') w = self.width() h = self.height() self._RenderWindow.SetSize(w, h) self._Iren.SetSize(w, h) self._Iren.ConfigureEvent() self.update() def _GetCtrlShift(self, ev): ctrl = shift = False if hasattr(ev, 'modifiers'): if ev.modifiers() & Qt.ShiftModifier: shift = True if ev.modifiers() & Qt.ControlModifier: ctrl = True else: if self.__saveModifiers & Qt.ShiftModifier: shift = True if self.__saveModifiers & Qt.ControlModifier: ctrl = True return ctrl, shift def enterEvent(self, ev): if not self.hasFocus(): self.__oldFocus = self.focusWidget() self.setFocus() ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, chr(0), 0, None) self._Iren.EnterEvent() def leaveEvent(self, ev): if self.__saveButtons == Qt.NoButton and self.__oldFocus: self.__oldFocus.setFocus() self.__oldFocus = None ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, chr(0), 0, None) self._Iren.LeaveEvent() def mousePressEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) repeat = 0 if ev.type() == QEvent.MouseButtonDblClick: repeat = 1 self._Iren.SetEventInformationFlipY(ev.x(), ev.y(), ctrl, shift, chr(0), repeat, None) self._ActiveButton = ev.button() if self._ActiveButton == Qt.LeftButton: self._Iren.LeftButtonPressEvent() elif self._ActiveButton == Qt.RightButton: self._Iren.RightButtonPressEvent() elif self._ActiveButton == Qt.MidButton: self._Iren.MiddleButtonPressEvent() def mouseReleaseEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(ev.x(), ev.y(), ctrl, shift, chr(0), 0, None) if self._ActiveButton == Qt.LeftButton: self._Iren.LeftButtonReleaseEvent() elif self._ActiveButton == Qt.RightButton: self._Iren.RightButtonReleaseEvent() elif self._ActiveButton == Qt.MidButton: self._Iren.MiddleButtonReleaseEvent() def mouseMoveEvent(self, ev): self.__saveModifiers = ev.modifiers() self.__saveButtons = ev.buttons() self.__saveX = ev.x() self.__saveY = ev.y() ctrl, shift = self._GetCtrlShift(ev) self._Iren.SetEventInformationFlipY(ev.x(), ev.y(), ctrl, shift, chr(0), 0, None) self._Iren.MouseMoveEvent() def keyPressEvent(self, ev): """ React to key pressed event. If event text contains multiple characters, it is truncated to first one. """ ctrl, shift = self._GetCtrlShift(ev) key_sym = _qt_key_to_key_sym(ev.key()) if ev.key() < 256: # Sometimes, the OS allows a chord (e.g. Alt-T) to generate # a Unicode character outside of the 8-bit Latin-1 range. We will # try to pass along Latin-1 characters unchanged, since VTK expects # a single `char` byte. If not, we will try to pass on the root key # of the chord (e.g. 'T' above). if ev.text() and ev.text() <= u'\u00ff': key = ev.text().encode('latin-1') else: # Has modifiers, but an ASCII key code. key = chr(ev.key()) else: key = chr(0) # Truncating key pressed to first character if slow machine leads to # multiple times the same key (required by SetEventInformationFlipY): if ev.isAutoRepeat(): key = key[0] self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, key, 0, key_sym) self._Iren.KeyPressEvent() self._Iren.CharEvent() def keyReleaseEvent(self, ev): ctrl, shift = self._GetCtrlShift(ev) key_sym = _qt_key_to_key_sym(ev.key()) if ev.key() < 256: if ev.text() and ev.text() <= u'\u00ff': key = ev.text().encode('latin-1') else: # Has modifiers, but an ASCII key code. key = chr(ev.key()) else: key = chr(0) self._Iren.SetEventInformationFlipY(self.__saveX, self.__saveY, ctrl, shift, key, 0, None) self._Iren.KeyReleaseEvent() def wheelEvent(self, ev): """ Reimplemented to work around scrolling bug in Mac. Work around https://bugreports.qt-project.org/browse/QTBUG-22269. Accumulate wheel events that are within a period of 25ms into a single event. Changes in buttons or modifiers, while a scroll is going on, are not handled, since they seem to be too much of a corner case to be worth handling. """ self.wheel_accumulator += ev.delta() self._saved_wheel_event_info = ( ev.pos(), ev.globalPos(), self.wheel_accumulator, ev.buttons(), ev.modifiers(), ev.orientation() ) ev.setAccepted(True) if not self.wheel_timer.isActive(): self.wheel_timer.start() def _emit_wheel_event(self): ev = QWheelEvent(*self._saved_wheel_event_info) if ev.delta() >= 0: self._Iren.MouseWheelForwardEvent() else: self._Iren.MouseWheelBackwardEvent() self.wheel_timer.stop() self.wheel_accumulator = 0 def GetRenderWindow(self): return self._RenderWindow def Render(self): self.update()
class QWaitingSpinner(QWidget): def __init__(self, parent, centerOnParent=True, disableParentWhenSpinning=False, modality=Qt.NonModal): # super().__init__(parent) QWidget.__init__(self, parent) self._centerOnParent = centerOnParent self._disableParentWhenSpinning = disableParentWhenSpinning # WAS IN initialize() self._color = QColor(Qt.black) self._roundness = 100.0 self._minimumTrailOpacity = 3.14159265358979323846 self._trailFadePercentage = 80.0 self._revolutionsPerSecond = 1.57079632679489661923 self._numberOfLines = 20 self._lineLength = 10 self._lineWidth = 2 self._innerRadius = 10 self._currentCounter = 0 self._isSpinning = False self._timer = QTimer(self) self._timer.timeout.connect(self.rotate) self.updateSize() self.updateTimer() self.hide() # END initialize() self.setWindowModality(modality) self.setAttribute(Qt.WA_TranslucentBackground) def paintEvent(self, QPaintEvent): self.updatePosition() painter = QPainter(self) painter.fillRect(self.rect(), Qt.transparent) painter.setRenderHint(QPainter.Antialiasing, True) if self._currentCounter >= self._numberOfLines: self._currentCounter = 0 painter.setPen(Qt.NoPen) for i in range(0, self._numberOfLines): painter.save() painter.translate(self._innerRadius + self._lineLength, self._innerRadius + self._lineLength) rotateAngle = float(360 * i) / float(self._numberOfLines) painter.rotate(rotateAngle) painter.translate(self._innerRadius, 0) distance = self.lineCountDistanceFromPrimary( i, self._currentCounter, self._numberOfLines) color = self.currentLineColor(distance, self._numberOfLines, self._trailFadePercentage, self._minimumTrailOpacity, self._color) painter.setBrush(color) rect = QRect(0, -self._lineWidth/2, self._lineLength, self._lineWidth) painter.drawRoundedRect( rect, self._roundness, self._roundness, Qt.RelativeSize) painter.restore() def start(self): self.updatePosition() self._isSpinning = True self.show() if self.parentWidget and self._disableParentWhenSpinning: self.parentWidget().setEnabled(False) if not self._timer.isActive(): self._timer.start() self._currentCounter = 0 def stop(self): self._isSpinning = False self.hide() if self.parentWidget() and self._disableParentWhenSpinning: self.parentWidget().setEnabled(True) if self._timer.isActive(): self._timer.stop() self._currentCounter = 0 def setNumberOfLines(self, lines): self._numberOfLines = lines self._currentCounter = 0 self.updateTimer() def setLineLength(self, length): self._lineLength = length self.updateSize() def setLineWidth(self, width): self._lineWidth = width self.updateSize() def setInnerRadius(self, radius): self._innerRadius = radius self.updateSize() def color(self): return self._color def roundness(self): return self._roundness def minimumTrailOpacity(self): return self._minimumTrailOpacity def trailFadePercentage(self): return self._trailFadePercentage def revolutionsPersSecond(self): return self._revolutionsPerSecond def numberOfLines(self): return self._numberOfLines def lineLength(self): return self._lineLength def lineWidth(self): return self._lineWidth def innerRadius(self): return self._innerRadius def isSpinning(self): return self._isSpinning def setRoundness(self, roundness): self._roundness = max(0.0, min(100.0, roundness)) def setColor(self, color=Qt.black): self._color = QColor(color) def setRevolutionsPerSecond(self, revolutionsPerSecond): self._revolutionsPerSecond = revolutionsPerSecond self.updateTimer() def setTrailFadePercentage(self, trail): self._trailFadePercentage = trail def setMinimumTrailOpacity(self, minimumTrailOpacity): self._minimumTrailOpacity = minimumTrailOpacity def rotate(self): self._currentCounter += 1 if self._currentCounter >= self._numberOfLines: self._currentCounter = 0 self.update() def updateSize(self): size = (self._innerRadius + self._lineLength) * 2 self.setFixedSize(size, size) def updateTimer(self): self._timer.setInterval( 1000/(self._numberOfLines*self._revolutionsPerSecond)) def updatePosition(self): if self.parentWidget() and self._centerOnParent: self.move(self.parentWidget().width() / 2 - self.width() / 2, self.parentWidget().height() / 2 - self.height() / 2) def lineCountDistanceFromPrimary(self, current, primary, totalNrOfLines): distance = primary - current if distance < 0: distance += totalNrOfLines return distance def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc, minOpacity, colorinput): color = QColor(colorinput) if countDistance == 0: return color minAlphaF = minOpacity / 100.0 distanceThreshold = int( math.ceil((totalNrOfLines-1)*trailFadePerc/100)) if countDistance > distanceThreshold: color.setAlphaF(minAlphaF) else: alphaDiff = color.alphaF() - minAlphaF gradient = alphaDiff / float(distanceThreshold + 1) resultAlpha = color.alphaF() - gradient * countDistance # If alpha is out of bounds, clip it. resultAlpha = min(1.0, max(0.0, resultAlpha)) color.setAlphaF(resultAlpha) return color
class MainWindow(QMainWindow): """Voice Changer main window.""" def __init__(self, parent=None): super(MainWindow, self).__init__() self.statusBar().showMessage("Move Dial to Deform Microphone Voice !.") self.setWindowTitle(__doc__) self.setMinimumSize(240, 240) self.setMaximumSize(480, 480) self.resize(self.minimumSize()) self.setWindowIcon(QIcon.fromTheme("audio-input-microphone")) self.tray = QSystemTrayIcon(self) self.center() QShortcut("Ctrl+q", self, activated=lambda: self.close()) self.menuBar().addMenu("&File").addAction("Quit", lambda: exit()) self.menuBar().addMenu("Sound").addAction( "STOP !", lambda: call('killall rec', shell=True)) windowMenu = self.menuBar().addMenu("&Window") windowMenu.addAction("Hide", lambda: self.hide()) windowMenu.addAction("Minimize", lambda: self.showMinimized()) windowMenu.addAction("Maximize", lambda: self.showMaximized()) windowMenu.addAction("Restore", lambda: self.showNormal()) windowMenu.addAction("FullScreen", lambda: self.showFullScreen()) windowMenu.addAction("Center", lambda: self.center()) windowMenu.addAction("Top-Left", lambda: self.move(0, 0)) windowMenu.addAction("To Mouse", lambda: self.move_to_mouse_position()) # widgets group0 = QGroupBox("Voice Deformation") self.setCentralWidget(group0) self.process = QProcess(self) self.process.error.connect( lambda: self.statusBar().showMessage("Info: Process Killed", 5000)) self.control = QDial() self.control.setRange(-10, 20) self.control.setSingleStep(5) self.control.setValue(0) self.control.setCursor(QCursor(Qt.OpenHandCursor)) self.control.sliderPressed.connect( lambda: self.control.setCursor(QCursor(Qt.ClosedHandCursor))) self.control.sliderReleased.connect( lambda: self.control.setCursor(QCursor(Qt.OpenHandCursor))) self.control.valueChanged.connect( lambda: self.control.setToolTip(f"<b>{self.control.value()}")) self.control.valueChanged.connect( lambda: self.statusBar().showMessage( f"Voice deformation: {self.control.value()}", 5000)) self.control.valueChanged.connect(self.run) self.control.valueChanged.connect(lambda: self.process.kill()) # Graphic effect self.glow = QGraphicsDropShadowEffect(self) self.glow.setOffset(0) self.glow.setBlurRadius(99) self.glow.setColor(QColor(99, 255, 255)) self.control.setGraphicsEffect(self.glow) self.glow.setEnabled(False) # Timer to start self.slider_timer = QTimer(self) self.slider_timer.setSingleShot(True) self.slider_timer.timeout.connect(self.on_slider_timer_timeout) # an icon and set focus QLabel(self.control).setPixmap( QIcon.fromTheme("audio-input-microphone").pixmap(32)) self.control.setFocus() QVBoxLayout(group0).addWidget(self.control) self.menu = QMenu(__doc__) self.menu.addAction(__doc__).setDisabled(True) self.menu.setIcon(self.windowIcon()) self.menu.addSeparator() self.menu.addAction( "Show / Hide", lambda: self.hide() if self.isVisible() else self.showNormal()) self.menu.addAction("STOP !", lambda: call('killall rec', shell=True)) self.menu.addSeparator() self.menu.addAction("Quit", lambda: exit()) self.tray.setContextMenu(self.menu) self.make_trayicon() def run(self): """Run/Stop the QTimer.""" if self.slider_timer.isActive(): self.slider_timer.stop() self.glow.setEnabled(True) call('killall rec ; killall play', shell=True) self.slider_timer.start(3000) def on_slider_timer_timeout(self): """Run subprocess to deform voice.""" self.glow.setEnabled(False) value = int(self.control.value()) * 100 command = f'play -q -V0 "|rec -q -V0 -n -d -R riaa bend pitch {value} "' print(f"Voice Deformation Value: {value}") print(f"Voice Deformation Command: {command}") self.process.start(command) if self.isVisible(): self.statusBar().showMessage("Minimizing to System TrayIcon", 3000) print("Minimizing Main Window to System TrayIcon now...") sleep(3) self.hide() def center(self): """Center Window on the Current Screen,with Multi-Monitor support.""" window_geometry = self.frameGeometry() mousepointer_position = QApplication.desktop().cursor().pos() screen = QApplication.desktop().screenNumber(mousepointer_position) centerPoint = QApplication.desktop().screenGeometry(screen).center() window_geometry.moveCenter(centerPoint) self.move(window_geometry.topLeft()) def move_to_mouse_position(self): """Center the Window on the Current Mouse position.""" window_geometry = self.frameGeometry() window_geometry.moveCenter(QApplication.desktop().cursor().pos()) self.move(window_geometry.topLeft()) def make_trayicon(self): """Make a Tray Icon.""" if self.windowIcon() and __doc__: self.tray.setIcon(self.windowIcon()) self.tray.setToolTip(__doc__) self.tray.activated.connect( lambda: self.hide() if self.isVisible() else self.showNormal()) return self.tray.show()
class MainWindow(QMainWindow): def __init__(self, conn_file, nodes, chnlgrp, config): super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.splitter_cmdinfo.setStretchFactor(0, 1) self.ui.splitter_h.setStretchFactor(0, 1) self.ui.splitter_v.setStretchFactor(0, 1) self.__rightSideBar_isShrinked = False self.__rightSideBar_lastIndex = self.ui.tabWidget_rightSideBar.currentIndex() self.__bottomSideBar_isShrinked = False self.__bottomSideBar_lastIndex = self.ui.tabWidget_bottomSideBar.currentIndex() self.node = nodes self.config = config self.highlight = {} node = nodes['mnode'][0] root = QTreeWidgetItem(None, [node]) if nodes['mnode'][1]: color = getattr(Qt, nodes['mnode'][1]) self.highlight[node] = color root.setBackground(0, color) self.ui.treeWidget_node.addTopLevelItem(root) for k, v in nodes['node'].items(): item = QTreeWidgetItem(root, [k]) item.setTextAlignment(0, Qt.AlignRight) nodes['node'][k]['item'] = item if v['color']: color = getattr(Qt, v['color']) item.setBackground(0, color) self.highlight[k] = color self.ui.treeWidget_node.addTopLevelItem(item) if nodes['xnode']: root = QTreeWidgetItem(None, ['xnode']) root.setTextAlignment(0, Qt.AlignRight) self.ui.treeWidget_node.addTopLevelItem(root) for k, v in nodes['xnode'].items(): item = QTreeWidgetItem(root, [k]) if v: color = getattr(Qt, v) item.setBackground(0, color) self.highlight[k] = color self.ui.treeWidget_node.addTopLevelItem(item) self.ui.treeWidget_node.expandAll() for i in range(self.ui.treeWidget_node.columnCount()): self.ui.treeWidget_node.resizeColumnToContents(i) if os.path.exists(config['rdebug']['mapfile']): self.rdbglst = rdebug.get_xval(config['rdebug']['mapfile']) for i in self.rdbglst: item = QTreeWidgetItem(None, [i[0], '%04X'%i[1], str(i[2])]) self.ui.treeWidget_rdbglst.addTopLevelItem(item) if i[0] == 'gParamData': self.ui.treeWidget_rdbglst.setCurrentItem(item) self.on_treeWidget_rdbglst_itemClicked(item, 0) for i in range(self.ui.treeWidget_rdbglst.columnCount()): self.ui.treeWidget_rdbglst.resizeColumnToContents(i) self.rdbgidx = 0 self.ui.comboBox_cmd.keyReleaseEvent = self.keyReleaseEvent self.buf = [] self.conn = None if conn_file: if isinstance(conn_file, str): if os.path.exists(conn_file): self.load_file(conn_file) else: self.conn = conn_file self.ui.comboBox_chnlgrp.setEnabled(True) self.ui.pushButton_parsepkt.setEnabled(True) self.ui.pushButton_rdbgsend.setEnabled(True) self.timer = QTimer(self) self.timer.timeout.connect(self.update) self.timer.start(100) self.upgsrc = self.config['upgrade']['srcaddr'][:12].lstrip('0') self.txtimer = QTimer(self) self.txtimer.setSingleShot(True) self.txtimer.timeout.connect(self.txpacket) self.ui.comboBox_chnlgrp.setCurrentIndex(chnlgrp) sendchnls = config['DEFAULT']['sendchannel'] self.__sendchnl = int(sendchnls, 16 if sendchnls.startswith('0x') else 10) self.__whosyourdaddy = os.path.exists('whosyourdaddy') if not self.__whosyourdaddy: self.ui.pushButton_rdbgsend.setEnabled(False) self.ui.pushButton_rfplcsw.setEnabled(False) def load_file(self, file): with open(file, 'rb') as f: self.buf = pickle.load(f) self.ui.treeWidget.clear() for i in self.buf: self.add_treeitem(i) for i in range(self.ui.treeWidget.columnCount()): self.ui.treeWidget.resizeColumnToContents(i) def add_treeitem(self, rdata): item = QTreeWidgetItem(None, rdata[0]) for i in range(6, 6+len(rdata[0][6:10])): if rdata[0][i] in self.highlight: item.setBackground(i, QBrush(self.highlight[rdata[0][i]])) item.setTextAlignment(i, Qt.AlignRight) self.ui.treeWidget.addTopLevelItem(item) # rowdata[0][?]: 6 - mdst, 7 - msrc, 8 - ndst, 9 - nsrc. if rdata[0][2] == 'acRdNdCfgUp': if rdata[0][9] in self.node['node']: self.node['node'][rdata[0][9]]['item'] .setText(1, rdata[1]['sVer']) self.ui.treeWidget_node.resizeColumnToContents(1) elif rdata[0][2] == 'acxRdVerInfoUp': if rdata[0][9] in self.node['node']: self.node['node'][rdata[0][9]]['item'] .setText(1, rdata[1]['sver']) self.node['node'][rdata[0][9]]['item'] .setText(2, rdata[1]['date']) self.node['node'][rdata[0][9]]['item'] .setText(3, rdata[1]['time']) self.ui.treeWidget_node.resizeColumnToContents(1) self.ui.treeWidget_node.resizeColumnToContents(2) self.ui.treeWidget_node.resizeColumnToContents(3) elif rdata[0][2].startswith('mcDbg'): if rdata[0][7] in self.node['node']: self.ui.plainTextEdit_rdbgresp.setPlainText(rdata[1]['dat']) #plainTextEdit def update(self): if self.conn.poll(): msg = self.conn.recv() if msg[0] == 'pkt': if msg[1][0] == 'parsepkt': if self.ui.treeWidget.topLevelItemCount() and self.ui.treeWidget.topLevelItem(0).text(0) == 'parsepkt': self.ui.treeWidget.takeTopLevelItem(0) item = QTreeWidgetItem(None, msg[1]) for i in range(self.ui.treeWidget.columnCount()): item.setBackground(i, QBrush(Qt.green)) if 5 < i < 10: item.setTextAlignment(i, Qt.AlignRight) self.ui.treeWidget.insertTopLevelItem(0, item) self.ui.treeWidget.scrollToTop() self.parsepkt = msg[1:] else: self.buf.append(msg[1:]) self.add_treeitem(msg[1:]) if self.ui.checkBox_autoscroll.isChecked(): self.ui.treeWidget.scrollToBottom() for i in range(self.ui.treeWidget.columnCount()): self.ui.treeWidget.resizeColumnToContents(i) elif msg[0] == 'msg': self.ui.plainTextEdit_log.appendPlainText('[msg]%s.' % msg[1]) elif msg[0] == 'err': self.ui.plainTextEdit_log.appendPlainText(msg[1]) @pyqtSlot(QTreeWidgetItem, int) def on_treeWidget_itemClicked(self, item, column): index = self.ui.treeWidget.indexOfTopLevelItem(item) self.ui.treeWidget_cmdinfo.clear() if self.ui.treeWidget.topLevelItem(0).text(0) == 'parsepkt': if index == 0: pktinfo = self.parsepkt else: pktinfo = self.buf[index-1] else: pktinfo = self.buf[index] if pktinfo[1]: self.ui.treeWidget_cmdinfo.addTopLevelItem(QTreeWidgetItem(None, ('cmdType', item.text(2)))) self.ui.treeWidget_cmdinfo.addTopLevelItem(QTreeWidgetItem(None, ('--', '--'))) for i in pktinfo[1].items(): self.ui.treeWidget_cmdinfo.addTopLevelItem(QTreeWidgetItem(None, i)) self.ui.treeWidget_cmdinfo.resizeColumnToContents(0) # routeLsg if column == 12: self.ui.plainTextEdit_pktdata.setPlainText(item.text(column)) else: self.ui.plainTextEdit_pktdata.setPlainText(pktinfo[2]) @pyqtSlot(QTreeWidgetItem, int) def on_treeWidget_cmdinfo_itemClicked(self, item, column): self.ui.plainTextEdit_pktdata.setPlainText(item.text(1)) @pyqtSlot(QPoint) def on_treeWidget_customContextMenuRequested(self, pos): popMenu =QMenu(self) if self.ui.treeWidget.itemAt(pos): column = self.ui.treeWidget.header().logicalIndexAt(pos) if 6 <= column <= 9: popMenu.addAction(self.ui.actionHighlight) popMenu.addSeparator() if self.ui.treeWidget.topLevelItemCount(): popMenu.addAction(self.ui.actionSaveAs) popMenu.addAction(self.ui.actionLoad) popMenu.addSeparator() popMenu.addAction(self.ui.actionClear) else: popMenu.addAction(self.ui.actionLoad) if self.ui.treeWidget.topLevelItemCount() and self.ui.treeWidget.topLevelItem(0).text(0) == 'parsepkt': popMenu.addSeparator() popMenu.addAction(self.ui.actionRmParsed) popMenu.popup(QCursor.pos()) @pyqtSlot() def on_actionClear_triggered(self): self.buf = [] self.ui.treeWidget.clear() @pyqtSlot() def on_actionSaveAs_triggered(self): t = datetime.now().strftime('%y-%m-%d_%H-%M-%S') file = QFileDialog.getSaveFileName(self, 'Save file', './dat/' + t, 'data file(*.dat)')[0] if file: with open(file, 'wb') as f: pickle.dump(self.buf, f) @pyqtSlot() def on_actionLoad_triggered(self): file = QFileDialog.getOpenFileName(self, 'Load file', './dat', 'data file(*.dat)')[0] if file: self.load_file(file) @pyqtSlot() def on_actionHighlight_triggered(self): color = QColorDialog.getColor(parent=self) if color.isValid(): item = self.ui.treeWidget.currentItem() column = self.ui.treeWidget.currentColumn() addr = item.text(column) self.highlight[addr] = color for i in range(self.ui.treeWidget.topLevelItemCount()): item = self.ui.treeWidget.topLevelItem(i) for j in range(6, 10): if addr == item.text(j): item.setBackground(j, color) @pyqtSlot() def on_actionRmParsed_triggered(self): self.ui.treeWidget.takeTopLevelItem(0) @pyqtSlot() def on_pushButton_parsepkt_clicked(self): text = self.ui.plainTextEdit_pktdata.toPlainText() self.conn.send(['parsepkt', bytearray.fromhex(text)]) @pyqtSlot() def on_actionRdSnCfg_triggered(self): if self.txtimer.isActive(): self.ui.plainTextEdit_log.appendPlainText('Tx busy.') return self.txpkt = [] for item in self.ui.treeWidget_node.selectedItems(): addr = item.text(0) if addr in self.node['node']: self.txpkt.append(upgrade.mk_rdsncfg(addr, self.upgsrc)) item.setText(1, '') if self.txpkt: self.txtimer.start(1000) @pyqtSlot() def on_actionRdVerInfo_triggered(self): if self.txtimer.isActive(): self.ui.plainTextEdit_log.appendPlainText('Tx busy.') return self.txpkt = [] for item in self.ui.treeWidget_node.selectedItems(): addr = item.text(0) if addr in self.node['node']: self.txpkt.append(upgrade.mk_rdverinfo(addr, self.upgsrc)) item.setText(1, '') if self.txpkt: self.txtimer.start(1000) @pyqtSlot() def on_actionRdbgPoolType_triggered(self): self.ui.plainTextEdit_rdbgresp.setPlainText('') items = self.ui.treeWidget_node.selectedItems() if items: addr = items[0].text(0) self.rdbgidx += 1 pkt = rdebug.mk_pooltype(addr, self.upgsrc, self.rdbgidx % 128) self.conn.send(['send', self.__sendchnl, pkt]) self.ui.plainTextEdit_log.appendPlainText('Tx:'+' '.join(['%02X'%i for i in pkt])) @pyqtSlot() def on_actionEraseParam_triggered(self): self.ui.plainTextEdit_rdbgresp.setPlainText('') items = self.ui.treeWidget_node.selectedItems() if items: addr = items[0].text(0) self.rdbgidx += 1 pkt = rdebug.mk_eraseparam(addr, self.upgsrc, self.rdbgidx % 128) self.conn.send(['send', self.__sendchnl, pkt]) self.ui.plainTextEdit_log.appendPlainText('Tx:'+' '.join(['%02X'%i for i in pkt])) def txpacket(self): if self.txpkt: self.conn.send(['send', self.__sendchnl, self.txpkt[0]]) self.ui.plainTextEdit_log.appendPlainText('Tx:'+' '.join(['%02X'%i for i in self.txpkt[0]])) del self.txpkt[0] self.txtimer.start(1000) @pyqtSlot(QTreeWidgetItem, int) def on_treeWidget_node_itemClicked(self, item, column): self.ui.lineEdit_curnode.setText(item.text(0)) @pyqtSlot(QPoint) def on_treeWidget_node_customContextMenuRequested(self, pos): if self.conn and self.ui.treeWidget_node.selectedItems(): popMenu =QMenu(self) popMenu.addAction(self.ui.actionRdSnCfg) popMenu.addAction(self.ui.actionRdVerInfo) if self.__whosyourdaddy: popMenu.addAction(self.ui.actionRdbgPoolType) popMenu.addSeparator() popMenu.addAction(self.ui.actionEraseParam) popMenu.popup(QCursor.pos()) @pyqtSlot(QTreeWidgetItem, int) def on_treeWidget_rdbglst_itemClicked(self, item, column): row = self.ui.treeWidget_rdbglst.indexOfTopLevelItem(item) self.ui.spinBox_rdbgaddr.setValue(self.rdbglst[row][1]) len = self.rdbglst[row][2] val = len if len < 128 else 128 self.ui.spinBox_rdbglen.setValue(val) @pyqtSlot() def on_pushButton_rdbgsend_clicked(self): self.ui.plainTextEdit_rdbgresp.setPlainText('') items = self.ui.treeWidget_node.selectedItems() if items: addr = items[0].text(0) self.rdbgidx += 1 pkt = rdebug.mk_rxval(addr, self.upgsrc, self.rdbgidx % 128, self.ui.spinBox_rdbgaddr.value(), self.ui.spinBox_rdbglen.value()) self.conn.send(['send', self.__sendchnl, pkt]) self.ui.plainTextEdit_log.appendPlainText('Tx:'+' '.join(['%02X'%i for i in pkt])) else: self.ui.plainTextEdit_log.appendPlainText('[rdbg]read xdata: Need select a node.') @pyqtSlot(int) def on_comboBox_chnlgrp_currentIndexChanged(self, index): self.conn.send(['setchnlgrp', index]) self.ui.plainTextEdit_log.appendPlainText('[msg]channel group set to %i.' % index) @pyqtSlot() def on_pushButton_rfplcsw_clicked(self): rfplc = 0 if self.ui.checkBox_rf.isChecked(): rfplc += 1 if self.ui.checkBox_plc.isChecked(): rfplc += 2 if rfplc: self.rdbgidx += 1 pkt = rdebug.mk_rfplcswitch(self.ui.lineEdit_curnode.text(), self.upgsrc, self.rdbgidx % 128, rfplc) self.conn.send(['send', self.__sendchnl, pkt]) self.ui.plainTextEdit_log.appendPlainText('Tx:'+' '.join(['%02X'%i for i in pkt])) else: self.ui.plainTextEdit_log.appendPlainText('[err]must select one in rf and plc.') def _CmdPro(self, s): if s.startswith('send '): cmd = s.split(maxsplit=2) if len(cmd) > 1: # try: cmd[1] = int(cmd[1], 16) cmd[2] = bytearray.fromhex(cmd[2]) self.conn.send(cmd) self.ui.plainTextEdit_log.appendPlainText( '[Tx]chnl(%i) %s.' % (cmd[1], ' '.join('%02X'%ii for ii in cmd[2]))) # except: # self.ui.plainTextEdit_log.appendPlainText('[error]send data error.\n') elif s.startswith('sendx '): cmd = s.split(maxsplit=3) if len(cmd) > 1: # try: cmd[1] = int(cmd[1], 16) cmd[2] = int(cmd[2]) cmd[3] = bytearray.fromhex(cmd[3]) self.conn.send(cmd) self.ui.plainTextEdit_log.appendPlainText( '[Tx]chnl(%02X-%i) %s.' % (cmd[1], cmd[2], ' '.join('%02X'%ii for ii in cmd[3]))) # except: # self.ui.plainTextEdit_log.appendPlainText('[error]send data error.\n') def keyReleaseEvent(self, e): key = e.key() if key == Qt.Key_Return: self._CmdPro(self.ui.comboBox_cmd.currentText()) self.ui.comboBox_cmd.setCurrentText('') @pyqtSlot() def on_pushButton_send_clicked(self): if self.ui.pushButton_send.text() == '发送': if self.ui.checkBox_autosend.isChecked(): self.autosendtimer = QTimer(self) self.autosendtimer.timeout.connect(self._AutoSend) self.autosendtimer.start(self.ui.spinBox_sendInterval.value() * 1000) self.ui.pushButton_send.setText('停止') else: self._AutoSend() else: self.autosendtimer.stop() self.ui.pushButton_send.setText('发送') def _AutoSend(self): self._CmdPro(self.ui.comboBox_cmd.currentText()) @pyqtSlot(int) def on_tabWidget_rightSideBar_tabBarClicked(self, index): if self.__rightSideBar_isShrinked: self.ui.tabWidget_rightSideBar.resize(self.__rightSideBar_bigSize) minSize = max(self.__rightSideBar_minSize, self.ui.tabWidget_rightSideBar.minimumSizeHint().width()) self.ui.tabWidget_rightSideBar.setMinimumWidth(minSize) self.ui.tabWidget_rightSideBar.setMaximumWidth(self.__rightSideBar_maxSize) self.ui.splitter_h.setSizes(self.__splitter_h_sizes) #self.ui.splitter_h.setStretchFactor(1, 1) self.__rightSideBar_isShrinked = False else: self.__rightSideBar_bigSize = self.ui.tabWidget_rightSideBar.size() if self.__rightSideBar_lastIndex == index: self.__rightSideBar_minSize = self.ui.tabWidget_rightSideBar.minimumSizeHint().width() self.__rightSideBar_maxSize = self.ui.tabWidget_rightSideBar.maximumWidth() self.__splitter_h_sizes = self.ui.splitter_h.sizes() self.ui.tabWidget_rightSideBar.setFixedWidth(self.ui.tabWidget_rightSideBar.tabBar().width()) self.ui.splitter_h.setStretchFactor(1, 1) self.__rightSideBar_isShrinked = True self.ui.tabWidget_rightSideBar.setCurrentIndex(index) self.__rightSideBar_lastIndex = index @pyqtSlot(int) def on_tabWidget_bottomSideBar_tabBarClicked(self, index): if self.__bottomSideBar_isShrinked: self.ui.tabWidget_bottomSideBar.resize(self.__bottomSideBar_bigSize) minSize = max(self.__bottomSideBar_minSize, self.ui.tabWidget_bottomSideBar.minimumSizeHint().height()) self.ui.tabWidget_bottomSideBar.setMinimumHeight(minSize) self.ui.tabWidget_bottomSideBar.setMaximumHeight(self.__bottomSideBar_maxSize) self.ui.splitter_v.setSizes(self.__splitter_v_sizes) self.ui.splitter_v.setStretchFactor(1, 1) self.__bottomSideBar_isShrinked = False else: self.__bottomSideBar_bigSize = self.ui.tabWidget_bottomSideBar.size() if self.__bottomSideBar_lastIndex == index: self.__bottomSideBar_minSize = self.ui.tabWidget_bottomSideBar.minimumSizeHint().height() self.__bottomSideBar_maxSize = self.ui.tabWidget_bottomSideBar.maximumHeight() self.__splitter_v_sizes = self.ui.splitter_v.sizes() self.ui.tabWidget_bottomSideBar.setFixedHeight(self.ui.tabWidget_bottomSideBar.tabBar().height()) self.ui.splitter_v.setStretchFactor(1, 1) self.__bottomSideBar_isShrinked = True self.ui.tabWidget_bottomSideBar.setCurrentIndex(index) self.__bottomSideBar_lastIndex = index @pyqtSlot() def on_pushButton_bcastAddr_clicked(self): self.ui.lineEdit_curnode.setText('FFFFFFFFFFFF')
class MainWindow(QWidget): __pressBaslat = False __timeStr = "" __top_zaman = 0 def __init__(self): super().__init__() self.setupUI() def setupUI(self): self.timer = QTimer() self.timer.setInterval(1000) self.timer.timeout.connect(self.geriSayim) self.setGeometry(300, 300, 415, 400) self.setWindowTitle(title) self.setWindowIcon(QIcon(pngAdres)) self.label = QLabel(self) self.label.setGeometry(QRect(155, 40, 30, 21)) self.label.setObjectName("label") self.label.setText("Süre : ") self.plainTextEdit = LineEdit(self) self.plainTextEdit.deselect() self.pushButton = QPushButton(self) self.pushButton.setGeometry(QRect(100, 120, 75, 23)) self.pushButton.setObjectName("pushButton") self.pushButton.setText("Başlat") self.pushButton2 = QPushButton(self) self.pushButton2.setGeometry(QRect(260, 120, 75, 23)) self.pushButton2.setObjectName("pushButton2") self.pushButton2.setText("Sıfırla") self.pushButton.clicked.connect(self.baslat) self.pushButton2.clicked.connect(self.sifirla) font = QFont() font.setPointSize(44) self.label_2 = QLabel(self) self.label_2.setGeometry(QRect(80, 180, 71, 61)) self.label_2.setObjectName("label_2") self.label_3 = QLabel(self) self.label_3.setGeometry(QRect(180, 180, 71, 61)) self.label_3.setObjectName("label_3") self.label_4 = QLabel(self) self.label_4.setGeometry(QRect(280, 180, 71, 61)) self.label_4.setObjectName("label_4") self.label_2.setText("00") self.label_3.setText("00") self.label_4.setText("00") self.label_2.setFont(font) self.label_3.setFont(font) self.label_4.setFont(font) self.label_5 = QLabel(self) self.label_5.setGeometry(QRect(150, 180, 21, 61)) self.label_5.setObjectName("label_5") self.label_6 = QLabel(self) self.label_6.setGeometry(QRect(250, 180, 21, 61)) self.label_6.setObjectName("label_6") self.label_5.setText(":") self.label_6.setText(":") self.label_5.setFont(font) self.label_6.setFont(font) font2 = QFont() font2.setPointSize(11) self.label_7 = QLabel(self) self.label_7.setGeometry(QRect(95, 260, 41, 21)) self.label_7.setFont(font2) self.label_7.setObjectName("label_7") self.label_8 = QLabel(self) self.label_8.setGeometry(QRect(187, 260, 61, 21)) self.label_8.setFont(font2) self.label_8.setObjectName("label_8") self.label_9 = QLabel(self) self.label_9.setGeometry(QRect(287, 260, 61, 21)) self.label_9.setFont(font2) self.label_9.setObjectName("label_9") self.label_7.setText("SAAT") self.label_8.setText("DAKİKA") self.label_9.setText("SANİYE") self.progressBar = QProgressBar(self) self.progressBar.setGeometry(QRect(33, 320, 350, 20)) self.progressBar.setProperty("value", 0) self.progressBar.setTextVisible(False) self.progressBar.setObjectName("progressBar") self.progressBar.setRange(0, 100) self.progressBar.setValue(0) self.show() def getTime(self): return self.plainTextEdit.text() def baslat(self): if self.getTime() == "__:__:__" or self.getTime() == "00:00:00": QMessageBox.critical(self, "Uyarı", "Lütfen süre girin.") return if self.__pressBaslat: # Zaman duraklatıldı self.pushButton.setText("Başlat") self.__pressBaslat = False self.timer.stop() else: # Zaman başlatıldı self.pushButton.setText("Duraklat") self.__pressBaslat = True if self.__timeStr == "": self.__timeStr = self.getTime() hour, min, sec = self.plainTextEdit.text().split(":") self.__top_zaman = int(hour) * 3600 + int(min) * 60 + int(sec) self.timer.start() self.geriSayim() def geriSayim(self): hour, min, sec = self.__timeStr.split(":") kalan_zaman = int(hour) * 3600 + int(min) * 60 + int(sec) if self.__timeStr == "00:00:00": self.pushButton.setText("Başlat") self.__pressBaslat = False self.timer.stop() self.__timeStr = "" self.uyari = Uyari() else: sec = str(int(sec) - 1) if sec == "-1": min = str(int(min) - 1) sec = "59" if min == "-1": hour = str(int(hour) - 1) min = "59" if len(sec) == 1: sec = "0" + sec if len(min) == 1: min = "0" + min if len(hour) == 1: hour = "0" + hour self.__timeStr = hour + ":" + min + ":" + sec bitenZaman = self.__top_zaman - kalan_zaman + 1 oran = int((bitenZaman / self.__top_zaman) * 100) self.progressBar.setValue(oran) self.label_2.setText(hour) self.label_3.setText(min) self.label_4.setText(sec) def sifirla(self): if self.timer and self.timer.isActive(): self.pushButton.setText("Başlat") self.__pressBaslat = False self.timer.stop() self.label_2.setText("00") self.label_3.setText("00") self.label_4.setText("00") self.progressBar.setValue(0) self.__timeStr = ""
class E5PassivePopup(QFrame): """ Class implementing dialog-like popup that displays messages without interrupting the user. """ Boxed = 0 Custom = 128 clicked = pyqtSignal((), (QPoint, )) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(E5PassivePopup, self).__init__(None) self.__popupStyle = DEFAULT_POPUP_TYPE self.__msgView = None self.__topLayout = None self.__hideDelay = DEFAULT_POPUP_TIME self.__hideTimer = QTimer(self) self.__autoDelete = False self.__fixedPosition = QPoint() self.setWindowFlags(POPUP_FLAGS) self.setFrameStyle(QFrame.Box | QFrame.Plain) self.setLineWidth(2) self.__hideTimer.timeout.connect(self.hide) self.clicked.connect(self.hide) def setView(self, child): """ Public method to set the message view. @param child reference to the widget to set as the message view (QWidget) """ self.__msgView = child self.__topLayout = QVBoxLayout(self) self.__topLayout.addWidget(self.__msgView) self.__topLayout.activate() def view(self): """ Public method to get a reference to the message view. @return reference to the message view (QWidget) """ return self.__msgView def setVisible(self, visible): """ Public method to show or hide the popup. @param visible flag indicating the visibility status (boolean) """ if not visible: super(E5PassivePopup, self).setVisible(visible) return if self.size() != self.sizeHint(): self.resize(self.sizeHint()) if self.__fixedPosition.isNull(): self.__positionSelf() else: self.move(self.__fixedPosition) super(E5PassivePopup, self).setVisible(True) delay = self.__hideDelay if delay < 0: delay = DEFAULT_POPUP_TIME if delay > 0: self.__hideTimer.start(delay) def show(self, p=None): """ Public slot to show the popup. @param p position for the popup (QPoint) """ if p is not None: self.__fixedPosition = p super(E5PassivePopup, self).show() def setTimeout(self, delay): """ Public method to set the delay for the popup is removed automatically. Setting the delay to 0 disables the timeout. If you're doing this, you may want to connect the clicked() signal to the hide() slot. Setting the delay to -1 makes it use the default value. @param delay value for the delay in milliseconds (integer) """ self.__hideDelay = delay if self.__hideTimer.isActive(): if delay: if delay == -1: delay = DEFAULT_POPUP_TIME self.__hideTimer.start(delay) else: self.__hideTimer.stop() def timeout(self): """ Public method to get the delay before the popup is removed automatically. @return the delay before the popup is removed automatically (integer) """ return self.__hideDelay def mouseReleaseEvent(self, evt): """ Protected method to handle a mouse release event. @param evt reference to the mouse event (QMouseEvent) """ self.clicked.emit() self.clicked.emit(evt.pos()) def hideEvent(self, evt): """ Protected method to handle the hide event. @param evt reference to the hide event (QHideEvent) """ self.__hideTimer.stop() def __defaultArea(self): """ Private method to determine the default rectangle to be passed to moveNear(). @return default rectangle (QRect) """ return QRect(100, 100, 200, 200) def __positionSelf(self): """ Private method to position the popup. """ self.__moveNear(self.__defaultArea()) def __moveNear(self, target): """ Private method to move the popup to be adjacent to the specified rectangle. @param target rectangle to be placed at (QRect) """ pos = self.__calculateNearbyPoint(target) self.move(pos.x(), pos.y()) def __calculateNearbyPoint(self, target): """ Private method to calculate the position to place the popup near the specified rectangle. @param target rectangle to be placed at (QRect) @return position to place the popup (QPoint) """ pos = target.topLeft() x = pos.x() y = pos.y() w = self.minimumSizeHint().width() h = self.minimumSizeHint().height() r = QApplication.desktop().screenGeometry( QPoint(x + w // 2, y + h // 2)) if x < r.center().x(): x += target.width() else: x -= w # It's apparently trying to go off screen, so display it ALL at the # bottom. if (y + h) > r.bottom(): y = r.bottom() - h if (x + w) > r.right(): x = r.right() - w if y < r.top(): y = r.top() if x < r.left(): x = r.left() return QPoint(x, y)
class Pager(QObject): """Provides an interface for paging in a View by page number. Pages are numbered starting with 1 in this api! """ currentPageChanged = pyqtSignal(int) pageCountChanged = pyqtSignal(int) def __init__(self, view): """Initializes the Pager with the View. Also connects with the Surface of the View and its Layout, so don't interchange them after initializing the Pager. """ super(Pager, self).__init__(view) self._currentPage = 0 self._pageCount = 0 self._blockLevel = 0 self._pageNumSet = False self._updateTimer = QTimer( singleShot=True, interval=100, timeout=self._updatePageNumber) # connect view.installEventFilter(self) view.surface().installEventFilter(self) view.surface().pageLayout().changed.connect(self._layoutChanged) # Connect to the kineticScrollingEnabled signal to avoid unneeded updates. view.kineticScrollingActive.connect(self.blockListening) def currentPage(self): """Returns the current page number (0 if there are no pages).""" return self._currentPage def setCurrentPage(self, num): """Shows the specified page number.""" changed, self._currentPage = self._currentPage != num, num self._pageNumSet = True self.blockListening(True) self.view().gotoPageNumber(num - 1) self.blockListening(False) if changed: self.currentPageChanged.emit(self._currentPage) def pageCount(self): """Returns the number of pages.""" return self._pageCount def view(self): return self.parent() def _layoutChanged(self): """Called internally whenever the layout is updated.""" layout = self.view().surface().pageLayout() self._pageCount, old = len(layout), self._pageCount if old != self._pageCount: self.pageCountChanged.emit(self._pageCount) self._updatePageNumber() def _updatePageNumber(self): """Called internally on layout change or view resize or surface move.""" self._currentPage, old = self.view().currentPageNumber() + 1, self._currentPage if self._currentPage == 0 and self._pageCount > 0: # the view may not be initialized self._currentPage = 1 if old != self._currentPage: self.currentPageChanged.emit(self._currentPage) def blockListening(self, block): """Block/unblock listening to event, used to avoid multiple updates when we know lots of events are going to be sent to the pager. Blocking can be nested, only the outermost unblock will really unblock the event processing.""" if block: self._blockLevel += 1 else: self._blockLevel -= 1 if self._blockLevel == 0: if self._pageNumSet: self._pageNumSet = False else: self._updatePageNumber() def eventFilter(self, obj, ev): if (self._blockLevel == 0 and ((ev.type() == QEvent.Resize and obj is self.view()) or (ev.type() == QEvent.Move and obj is self.view().surface())) and not self._updateTimer.isActive()): self._updateTimer.start() return False
class PositionIndicator(QToolButton): """Indicator, which shows text "Line: yy Column: xx" """ def __init__(self, parent): QToolButton.__init__(self, parent) self.setToolTip(self.tr("Cursor position")) self.setEnabled(False) self._setCursorPosition(-1, -1) minWidth = QFontMetrics(self.font()).width("Line: xxxxx Column: xxx") minWidth += 30 # for the button borders self.setMinimumWidth(minWidth) # Avoid flickering when text width changed core.workspace().currentDocumentChanged.connect(self._onCurrentDocumentChanged) core.workspace().cursorPositionChanged.connect(self._onCursorPositionChanged) self._timer = QTimer() self._timer.setInterval(200) self._timer.setSingleShot(True) self._timer.timeout.connect(self._onUpdatePositionTimer) self._passedUpdate = False def terminate(self): if self._timer.isActive(): self._timer.stop() core.workspace().currentDocumentChanged.disconnect(self._onCurrentDocumentChanged) core.workspace().cursorPositionChanged.disconnect(self._onCursorPositionChanged) def _onUpdatePositionTimer(self): """Update text on GUI according to current position """ if self._passedUpdate: document = core.workspace().currentDocument() self._setCursorPosition(*document.qutepart.cursorPosition) self._passedUpdate = False def _onCursorPositionChanged(self, document): """Cursor position changed. Update it now or schedule update later """ if self._timer.isActive(): self._passedUpdate = True else: self._setCursorPosition(*document.qutepart.cursorPosition) self._timer.start() # one more update after timeout. def _onCurrentDocumentChanged(self, oldDocument, currentDocument): """Current document has been changed """ if self._timer.isActive(): self._timer.stop() # Update connections if oldDocument is not None: self.clicked.disconnect(oldDocument.invokeGoTo) if currentDocument is not None: self.clicked.connect(currentDocument.invokeGoTo) # Update info if currentDocument is not None: self._setCursorPosition(*currentDocument.qutepart.cursorPosition) self.setEnabled(True) else: self._setCursorPosition(-1, -1) self.setEnabled(False) def _setCursorPosition(self, line, col): """Update cursor position on GUI. """ template = self.tr("Line: %s Column: %s") if line != -1 and col != -1: line = str(line + 1) col = str(col) else: line = '-' col = '-' self.setText(template % (line, col))
class Folder(QObject): """Manages the folding of a QTextDocument. You should inherit from this class to provide folding events. It is enough to implement the fold_events() method. By default, simple caching is used to store the nesting depth every 20 lines. This makes the depth() method faster, which would otherwise count the fold_events() for every block from the beginning of the document. The depth() caching expects that the fold_events that a text block generates do not depend on the contents of a text block later in the document. If your fold_events() method generates events for a text block that depend on a later block, you should disable caching by setting the cache_depth_lines instance (or class) attribute to zero. """ # cache depth() for every n lines (0=disable) cache_depth_lines = 20 def __init__(self, doc): QObject.__init__(self, doc) self._depth_cache = [] # cache result of depth() self._all_visible = None # True when all are certainly visible doc.contentsChange.connect(self.slot_contents_change) self._timer = QTimer(singleShot=True, timeout=self.check_consistency) @classmethod def find(cls, doc): for c in doc.children(): if type(c) is cls: return c @classmethod def get(cls, doc): return cls.find(doc) or cls(doc) def slot_contents_change(self, position, removed, added): """Called when the document changes. Provides limited support for unhiding regions when the user types text in it, and deletes the depth() cache for lines from position. """ block = self.document().findBlock(position) if self.cache_depth_lines: chunk = block.blockNumber() // self.cache_depth_lines del self._depth_cache[chunk:] if self._all_visible: return if not block.isVisible(): self.ensure_visible(block) else: n = block.next() if n.isValid() and not n.isVisible() and not self.fold_level(block).start: # the block is visible, but not the next. Just unfold the lines, # skipping sub-regions, until a visible block is encountered. self.mark(block, False) while n.isValid() and not n.isVisible(): n.setVisible(True) if self.fold_level(n).start and self.mark(n): r = self.region(n) if r: n = r.end if self.fold_level(n).start: continue n = n.next() start = block.next().position() self.document().markContentsDirty(start, n.position() - start) self._timer.start(250 + self.document().blockCount()) def invalidate_depth_cache(self, block): """Makes sure the depth is recomputed from the specified block.""" if self.cache_depth_lines: chunk = block.blockNumber() // self.cache_depth_lines del self._depth_cache[chunk:] def check_consistency(self): """Called some time after the last document change. Walk through the whole document, unfolding folded lines that - are in the toplevel - are in regions that have visible lines - are in regions that have visible sub-regions """ show_blocks = set() self._all_visible = True # for now at least ... def blocks_gen(): """Yield depth (before block), block and fold_level per block.""" depth = 0 for b in cursortools.all_blocks(self.document()): l = self.fold_level(b) yield depth, b, l depth += sum(l) blocks = blocks_gen() def check_region(start_block, start_depth): """Check a region from a starting block for visible lines. Return a four-tuple (must_show, depth, block, level). must_show is True if the region contains visible lines (in that case the invisible lines are already made visible) depth, block, and level are the result of the last block_gen yield. If level.start is True, a new region starts on the same line the former one ends. """ must_show = False invisible_blocks = [] start_block.isVisible() or invisible_blocks.append(start_block) for depth, block, level in blocks: block.isVisible() or invisible_blocks.append(block) if depth + level.stop < start_depth: # the region ends if block.isVisible() and not level.start: must_show = True if must_show: show_blocks.update(invisible_blocks) elif invisible_blocks: self._all_visible = False return must_show, depth, block, level elif block.isVisible(): must_show = True while level.start: must_show_region, depth, block, level = check_region(block, depth + sum(level)) if must_show_region: must_show = True # happens if region is not closed return must_show, 0, None, Level(0, 0) # toplevel for depth, block, level in blocks: block.isVisible() or show_blocks.add(block) while level.start: must_show, depth, block, level = check_region(block, depth + sum(level)) if show_blocks: for block in show_blocks: block.setVisible(True) start = min(show_blocks).position() end = max(show_blocks).position() self.document().markContentsDirty(start, end - start) def document(self): """Return our document.""" return self.parent() def fold_events(self, block): """Return an iterable of fold events for the block. An event is simply an integer constant START or END. The default implementation considers '{' as a start and '}' as end. """ for c in block.text(): if c == '{': yield START elif c == '}': yield STOP def fold_level(self, block): """Returns a named two-tuple Level(stop, start) about the block. stop is the number (negative!) of fold-levels that end in that block, start is the number of fold-levels that start in that block. This methods uses fold_events() to get the information, it discards folding regions that start and stop on the same text line. """ start, stop = 0, 0 for e in self.fold_events(block): if e is START: start += 1 elif start: start -= 1 else: stop -= 1 return Level(stop, start) def depth(self, block): """Return the number of active regions at the start of this block. The default implementation simply counts all the fold_events from the beginning of the document, using caching if the cache_depth_lines instance attribute is set to a value > 0. """ depth = 0 last = block.document().firstBlock() if self.cache_depth_lines: chunk = block.blockNumber() // self.cache_depth_lines if chunk: target = block.document().findBlockByNumber(chunk * self.cache_depth_lines) if chunk <= len(self._depth_cache): depth = self._depth_cache[chunk - 1] last = target else: # some values need to be computed first if self._depth_cache: depth = self._depth_cache[-1] last = block.document().findBlockByNumber(len(self._depth_cache) * self.cache_depth_lines) while last < target: depth += sum(self.fold_events(last)) last = last.next() if last.blockNumber() % self.cache_depth_lines == 0: self._depth_cache.append(depth) while last < block: depth += sum(self.fold_events(last)) last = last.next() return depth def region(self, block, depth=0): """Return as Region (start, end) the region of the specified block. start is the block the region starts, end the block the region ends. When collapsing the block, don't hide the last block if it starts a new fold region. The depth argument specifies how deep a region may be nested. The default value 0 searches the first containing region, 1 tries to find one more above that, etc. Use -1 to get the top-most region. """ start = None start_depth = 0 count = 0 for b in cursortools.backwards(block): l = self.fold_level(b) if l.start: count += l.start if count > start_depth: start = b start_depth = count if count > depth > -1: break count += l.stop if start: count = start_depth end = None for end in cursortools.forwards(block.next()): l = self.fold_level(end) if count <= -l.stop: return Region(start, end) count += sum(l) if end: return Region(start, end) def fold(self, block, depth=0): """Fold the region the block is in. The depth argument specifies how deep a region may be nested. The default value 0 searches the first containing region, 1 tries to find one more above that, etc. Use -1 to get the top-most region. """ r = self.region(block, depth) if not r: return # if the last block starts a new region, don't hide it count = 0 end = r.end.previous() if self.fold_level(r.end).start else r.end # don't hide the first block of the region for block in cursortools.forwards(r.start.next(), end): block.setVisible(False) self.mark(r.start, True) start = r.start.next().position() self.document().markContentsDirty(start, end.position() - start) self._all_visible = False def unfold(self, block, depth=0, full=False): """Unfolds the region the block is in. (Most times the block will be the first block of the region.) If multiple regions start at the same starting block, they will unfold all. The depth argument specifies how deep a region may be nested. The default value 0 searches the first containing region, 1 tries to find one more above that, etc. Use -1 to get the top-most region. If full is False (the default) sub-regions that were collapsed remain collapsed (provided that the mark() method is implemented). """ r = self.region(block, depth) if not r: return blocks = cursortools.forwards(r.start, r.end) for block in blocks: # is there a sub-region? then skip if marked as collapsed if block not in r: l = self.fold_level(block) if l.start: if full: self.mark(block, False) elif self.mark(block): count = l.start for b in blocks: l = self.fold_level(b) if count <= -l.stop: break count += sum(l) block.setVisible(True) self.mark(r.start, False) start = r.start.position() self.document().markContentsDirty(start, r.end.position() - start) def fold_toplevel(self): """Folds all toplevel regions, without touching inner regions.""" for block in cursortools.all_blocks(self.document()): if not block.isVisible(): continue elif self.fold_level(block).start: self.fold(block) def fold_all(self): """Folds all regions.""" for block in cursortools.all_blocks(self.document()): if self.fold_level(block).start: if block.isVisible(): self.fold(block) else: self.mark(block, True) def unfold_all(self): """Fully unfolds the document.""" first = None for block in cursortools.all_blocks(self.document()): if self.fold_level(block).start: self.mark(block, False) if not block.isVisible(): block.setVisible(True) if first is None: first = block last = block if first: self.document().markContentsDirty(first.position(), last.position() - first.position()) # no need to check consistency self._all_visible = True self._timer.isActive() and self._timer.stop() def mark(self, block, state=None): """This can be used to remember the folded state of a block. When folding or unfolding a block, this method is called with the first block and state True (for fold) or False (for unfold). When unfolding a region, this method is called for every block without state value, and its return value is checked. If the value evaluates to True, the sub-region will remain collapsed. The default implementation does, and returns, nothing. """ pass def ensure_visible(self, block): """Unfolds everything needed to make just the block visible.""" if block.isVisible(): return for d in range(self.depth(block), -1, -1): self.unfold(block, d) if block.isVisible(): return
class OneShotCallbackProxy(QObject): """ A proxy object that allows JavaScript to run Python callbacks. This creates a JavaScript-compatible object (can be added to `window`) that has functions `resume()` and `error()` that can be connected to Python callbacks. It is "one shot" because either `resume()` or `error()` should be called exactly _once_. It raises an exception if the combined number of calls to these methods is greater than 1. If timeout is zero, then the timeout is disabled. """ def __init__(self, parent, callback, errback, timeout=0): self.name = str(uuid.uuid1()) self._used_up = False self._callback = callback self._errback = errback if timeout < 0: raise ValueError('OneShotCallbackProxy timeout must be >= 0.') elif timeout == 0: self._timer = None elif timeout > 0: self._timer = QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self._timed_out) self._timer.start(timeout * 1000) super(OneShotCallbackProxy, self).__init__(parent) @pyqtSlot('QVariantMap') def resume(self, value=None): if self._used_up: raise OneShotCallbackError("resume() called on a one shot" " callback that was already used up.") self.use_up() self._callback(qt2py(value)) @pyqtSlot(str, bool) def error(self, message, raise_=False): if self._used_up: raise OneShotCallbackError("error() called on a one shot" " callback that was already used up.") self.use_up() self._errback(message, raise_) def cancel(self, reason): self.use_up() self._errback("One shot callback canceled due to: %s." % reason, raise_=False) def _timed_out(self): self.use_up() self._errback("One shot callback timed out while waiting for" " resume() or error().", raise_=False) def use_up(self): self._used_up = True if self._timer is not None and self._timer.isActive(): self._timer.stop()
class Surface(QWidget): rightClicked = pyqtSignal(QPoint) linkClicked = pyqtSignal(QEvent, page.Page, popplerqt5.Poppler.Link) linkHovered = pyqtSignal(page.Page, popplerqt5.Poppler.Link) linkLeft = pyqtSignal() linkHelpRequested = pyqtSignal(QPoint, page.Page, popplerqt5.Poppler.Link) selectionChanged = pyqtSignal(QRect) def __init__(self, view): super(Surface, self).__init__(view) self.setBackgroundRole(QPalette.Dark) self._view = weakref.ref(view) self._currentLinkId = None self._selecting = False self._magnifying = False self._magnifier = None self.setMagnifier(magnifier.Magnifier()) self.setMagnifierModifiers(Qt.CTRL) self._selection = QRect() self._rubberBand = CustomRubberBand(self) self._scrolling = False self._scrollTimer = QTimer(interval=100, timeout=self._scrollTimeout) self._pageLayout = None self._highlights = weakref.WeakKeyDictionary() self.setPageLayout(layout.Layout()) self.setContextMenuPolicy(Qt.PreventContextMenu) self.setLinksEnabled(True) self.setSelectionEnabled(True) self.setShowUrlTips(True) self.view().cursorNeedUpdate.connect(self.updateCursor) def pageLayout(self): return self._pageLayout def setPageLayout(self, layout): old, self._pageLayout = self._pageLayout, layout if old: old.redraw.disconnect(self.redraw) old.changed.disconnect(self.updateLayout) layout.redraw.connect(self.redraw) layout.changed.connect(self.updateLayout) def view(self): """Returns our associated View.""" return self._view() def viewportRect(self): """Returns the rectangle of us that is visible in the View.""" return self.view().viewport().rect().translated(-self.pos()) def setSelectionEnabled(self, enabled): """Enables or disables selecting rectangular regions.""" self._selectionEnabled = enabled if not enabled: self.clearSelection() self._rubberBand.hide() self._selecting = False def selectionEnabled(self): """Returns True if selecting rectangular regions is enabled.""" return self._selectionEnabled def setLinksEnabled(self, enabled): """Enables or disables the handling of Poppler.Links in the pages.""" self._linksEnabled = enabled def linksEnabled(self): """Returns True if the handling of Poppler.Links in the pages is enabled.""" return self._linksEnabled def setShowUrlTips(self, enabled): """Enables or disables showing the URL in a tooltip when hovering a link. (Of course also setLinksEnabled(True) if you want this.) """ self._showUrlTips = enabled def showUrlTips(self): """Returns True if URLs are shown in a tooltip when hovering a link.""" return self._showUrlTips def setMagnifier(self, magnifier): """Sets the Magnifier to use (or None to disable the magnifier). The Surface takes ownership of the Magnifier. """ if self._magnifier: self._magnifier.setParent(None) magnifier.setParent(self.view()) self._magnifier = magnifier def magnifier(self): """Returns the currently set magnifier.""" return self._magnifier def setMagnifierModifiers(self, modifiers): """Sets the modifiers (e.g. Qt.CTRL) to show the magnifier. Use None to show the magnifier always (instead of dragging). """ self._magnifierModifiers = modifiers def magnifierModifiers(self): """Returns the currently set keyboard modifiers (e.g. Qt.CTRL) to show the magnifier.""" return self._magnifierModifiers def hasSelection(self): """Returns True if there is a selection.""" return bool(self._selection) def setSelection(self, rect): """Sets the selection rectangle.""" rect = rect.normalized() old, self._selection = self._selection, rect self._rubberBand.setVisible(rect.isValid()) self._rubberBand.setGeometry(rect) if rect != old: self.selectionChanged.emit(rect) def selection(self): """Returns the selection rectangle (normalized) or an invalid QRect().""" return QRect(self._selection) def clearSelection(self): """Hides the selection rectangle.""" self.setSelection(QRect()) def selectedPages(self): """Return a list of the Page objects the selection encompasses.""" return list(self.pageLayout().pagesAt(self.selection())) def selectedPage(self): """Return the Page thas is selected for the largest part, or None.""" pages = self.selectedPages() if not pages: return def key(page): size = page.rect().intersected(self.selection()).size() return size.width() + size.height() return max(pages, key = key) def selectedPageRect(self, page): """Return the QRect on the page that falls in the selection.""" return self.selection().normalized().intersected(page.rect()).translated(-page.pos()) def selectedText(self): """Return all text falling in the selection.""" return '\n'.join(page.text(self.selection()) for page in self.selectedPages()) def redraw(self, rect): """Called when the Layout wants to redraw a rectangle.""" self.update(rect) def updateLayout(self): """Conforms ourselves to our layout (that must already be updated.)""" self.clearSelection() self.resize(self._pageLayout.size()) self.update() def highlight(self, highlighter, areas, msec=0): """Highlights the list of areas using the given highlighter. Every area is a two-tuple (page, rect), where rect is a rectangle inside (0, 0, 1, 1) like the linkArea attribute of a Poppler.Link. """ d = collections.defaultdict(list) for page, area in areas: d[page].append(area) d = weakref.WeakKeyDictionary(d) if msec: def clear(selfref=weakref.ref(self)): self = selfref() if self: self.clearHighlight(highlighter) t = QTimer(singleShot = True, timeout = clear) t.start(msec) else: t = None self.clearHighlight(highlighter) self._highlights[highlighter] = (d, t) self.update(sum((page.rect() for page in d), QRegion())) def clearHighlight(self, highlighter): """Removes the highlighted areas of the given highlighter.""" try: (d, t) = self._highlights[highlighter] except KeyError: return del self._highlights[highlighter] self.update(sum((page.rect() for page in d), QRegion())) def paintEvent(self, ev): """Handle PaintEvent on the surface to highlight the selection.""" painter = QPainter(self) pages = list(self.pageLayout().pagesAt(ev.rect())) for page in pages: page.paint(painter, ev.rect()) for highlighter, (d, t) in self._highlights.items(): rects = [] for page in pages: try: rects.extend(map(page.linkRect, d[page])) except KeyError: continue if rects: highlighter.paintRects(painter, rects) def handleMousePressEvent(self, ev): """Handle mouse press for various operations - links to source, - magnifier, - selection highlight, If event was used, return true to indicate processing should stop. """ # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) # selecting? if self._selectionEnabled: if self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge == _OUTSIDE: self.clearSelection() else: if ev.button() != Qt.RightButton or edge != _INSIDE: self._selecting = True self._selectionEdge = edge self._selectionRect = self.selection() self._selectionPos = pos if edge == _INSIDE: self.setCursor(Qt.SizeAllCursor) return True if not self._selecting: if ev.modifiers() & Qt.ShiftModifier and ev.button() == Qt.LeftButton and self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True if ev.button() == Qt.RightButton or int(ev.modifiers()) & _SCAM: if not (int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._selecting = True self._selectionEdge = _RIGHT | _BOTTOM self._selectionRect = QRect(pos, QSize(0, 0)) self._selectionPos = pos return True # link? if self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: self.linkClickEvent(ev, page, link) return True # magnifier? if (self._magnifier and int(ev.modifiers()) & _SCAM == self._magnifierModifiers and ev.button() == Qt.LeftButton): self._magnifier.moveCenter(pos) self._magnifier.show() self._magnifier.raise_() self._magnifying = True self.setCursor(QCursor(Qt.BlankCursor)) return True return False def handleMouseReleaseEvent(self, ev): """Handle mouse release events for various operations: - hide magnifier, - selection. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: self._magnifier.hide() self._magnifying = False self.unsetCursor() consumed = True elif self._selecting: self._selecting = False selection = self._selectionRect.normalized() if selection.width() < 8 and selection.height() < 8: self.clearSelection() else: self.setSelection(selection) if self._scrolling: self.stopScrolling() self.unsetCursor() consumed = True if ev.button() == Qt.RightButton: # As the event comes from the view, we need to map it locally. self.rightClick(self.mapFromParent(ev.pos())) consumed = True return consumed def handleMouseMoveEvent(self, ev): """Handle mouse move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False if self._magnifying: # As the event comes from the view, we need to map it locally. self._magnifier.moveCenter(self.mapFromParent(ev.pos())) consumed = True elif self._selecting: # As the event comes from the view, we need to map it locally. pos = self.mapFromParent(ev.pos()) self._moveSelection(pos) self._rubberBand.show() # check if we are dragging close to the edge of the view, scroll if needed view = self.viewportRect() dx = pos.x() - view.left() - 12 if dx >= 0: dx = pos.x() - view.right() + 12 if dx < 0: dx = 0 dy = pos.y() - view.top() - 12 if dy >= 0: dy = pos.y() - view.bottom() + 12 if dy < 0: dy = 0 if dx or dy: self.startScrolling(dx, dy) elif self._scrolling: self.stopScrolling() consumed = True return consumed def handleMoveEvent(self, ev): """Handle move events for various operations: - move magnifier, - selection extension. If event was used, return true to indicate processing should stop. """ consumed = False pos = self.mapFromGlobal(QCursor.pos()) if self._selecting: self._moveSelection(pos) consumed = True elif self._magnifying: self._magnifier.moveCenter(pos) consumed = True return consumed def handleHelpEvent(self, ev): """Handle help event: show link if any.""" if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromParent(ev.pos())) if link: self.linkHelpEvent(ev.globalPos(), page, link) return True def updateKineticCursor(self, active): """Cursor handling when kinetic move starts/stops. - reset the cursor and hide tooltips if visible at start, - update the cursor and show the appropriate tooltips at stop. Used as a slot linked to the kineticStarted() signal. """ if active: self.unsetCursor() if QToolTip.isVisible(): QToolTip.hideText() else: self.updateCursor(QCursor.pos()) if self._linksEnabled: page, link = self.pageLayout().linkAt(self.mapFromGlobal(QCursor.pos())) if link: self.linkHelpEvent(QCursor.pos(), page, link) def updateCursor(self, evpos): """Set the cursor to the right glyph, depending on action""" pos = self.mapFromGlobal(evpos) cursor = None edge = _OUTSIDE if self._selectionEnabled and self.hasSelection(): edge = selectionEdge(pos, self.selection()) if edge is not _OUTSIDE: if edge in (_TOP, _BOTTOM): cursor = Qt.SizeVerCursor elif edge in (_LEFT, _RIGHT): cursor = Qt.SizeHorCursor elif edge in (_LEFT | _TOP, _RIGHT | _BOTTOM): cursor = Qt.SizeFDiagCursor elif edge in (_TOP | _RIGHT, _BOTTOM | _LEFT): cursor = Qt.SizeBDiagCursor elif edge is _INSIDE: cursor = Qt.SizeAllCursor elif self._linksEnabled: page, link = self.pageLayout().linkAt(pos) if link: cursor = Qt.PointingHandCursor lid = id(link) else: lid = None if lid != self._currentLinkId: if self._currentLinkId is not None: self.linkHoverLeave() self._currentLinkId = lid if link: self.linkHoverEnter(page, link) self.setCursor(cursor) if cursor else self.unsetCursor() def linkHelpEvent(self, globalPos, page, link): """Called when a QHelpEvent occurs on a link. The default implementation shows a tooltip if showUrls() is True, and emits the linkHelpRequested() signal. """ if self._showUrlTips and isinstance(link, popplerqt5.Poppler.LinkBrowse): QToolTip.showText(globalPos, link.url(), self, page.linkRect(link.linkArea())) self.linkHelpRequested.emit(globalPos, page, link) def rightClick(self, pos): """Called when the right mouse button is released. (Use this instead of the contextMenuEvent as that one also fires when starting a right-button selection.) The default implementation emits the rightClicked(pos) signal and also sends a ContextMenu event to the View widget. """ self.rightClicked.emit(pos) QApplication.postEvent(self.view().viewport(), QContextMenuEvent(QContextMenuEvent.Mouse, pos + self.pos())) def linkClickEvent(self, ev, page, link): """Called when a link is clicked. The default implementation emits the linkClicked(event, page, link) signal. """ self.linkClicked.emit(ev, page, link) def linkHoverEnter(self, page, link): """Called when the mouse hovers over a link. The default implementation emits the linkHovered(page, link) signal. """ self.linkHovered.emit(page, link) def linkHoverLeave(self): """Called when the mouse does not hover a link anymore. The default implementation emits the linkLeft() signal. """ self.linkLeft.emit() def startScrolling(self, dx, dy): """Starts scrolling dx, dy about 10 times a second. Stops automatically when the end is reached. """ self._scrolling = QPoint(dx, dy) self._scrollTimer.isActive() or self._scrollTimer.start() def stopScrolling(self): """Stops scrolling.""" self._scrolling = False self._scrollTimer.stop() def _scrollTimeout(self): """(Internal) Called by the _scrollTimer.""" # change the scrollbars, but check how far they really moved. pos = self.pos() self.view().fastScrollBy(self._scrolling) diff = pos - self.pos() if not diff: self.stopScrolling() def _moveSelection(self, pos): """(Internal) Moves the dragged selection edge or corner to the given pos (QPoint).""" diff = pos - self._selectionPos self._selectionPos = pos edge = self._selectionEdge self._selectionRect.adjust( diff.x() if edge & _LEFT else 0, diff.y() if edge & _TOP else 0, diff.x() if edge & _RIGHT else 0, diff.y() if edge & _BOTTOM else 0) rect = self._selectionRect.normalized() self._rubberBand.setVisible(rect.isValid()) self._rubberBand.setGeometry(rect) if self.cursor().shape() in (Qt.SizeBDiagCursor, Qt.SizeFDiagCursor): # we're dragging a corner, use correct diagonal cursor bdiag = (edge in (3, 12)) ^ (self._selectionRect.width() * self._selectionRect.height() >= 0) self.setCursor(Qt.SizeBDiagCursor if bdiag else Qt.SizeFDiagCursor)
class TabSearch(_ScrollGroup): """Custom tab widget.""" def __init__(self, parent=None, *args, **kwargs): """Init class custom tab widget.""" super(TabSearch, self).__init__(self, *args, **kwargs) self.parent = parent self.setParent(parent) list1 = [_ for _ in UNICODEMOTICONS.values() if isinstance(_, str)] list2 = [_[1] for _ in entities.html5.items()] self.emos = tuple(sorted(set(list1 + list2))) # Timer to start self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self._make_search_unicode) self.search, layout = QLineEdit(self), self.layout() self.search.setPlaceholderText(" Search Unicode . . .") font = self.search.font() font.setPixelSize(25) font.setBold(True) self.search.setFont(font) self.search.setFocus() self.search.textChanged.connect(self._go) layout.addWidget(self.search) self.container, self.searchbutons, row, index = QWidget(self), [], 0, 0 self.container.setLayout(QGridLayout()) layout.addWidget(self.container) for i in range(50): button = QPushButton("?", self) button.released.connect(self.hide) button.setFlat(True) button.setDisabled(True) font = button.font() font.setPixelSize(25) button.setFont(font) index = index + 1 # cant use enumerate() row = row + 1 if not index % 8 else row self.searchbutons.append(button) self.container.layout().addWidget(button, row, index % 8) def _go(self): """Run/Stop the QTimer.""" if self.timer.isActive(): self.timer.stop() return self.timer.start(1000) def _make_search_unicode(self): """Make a search for Unicode Emoticons.""" search = str(self.search.text()).lower().strip() if search and len(search): found_exact = [_ for _ in self.emos if search in _] found_by_name = [] for emoticons_list in self.emos: for emote in emoticons_list: emojiname = str(self.parent.get_description(emote)).lower() if search in emojiname and len(emojiname): found_by_name += emote results = tuple(sorted(set(found_exact + found_by_name)))[:50] for emoji, button in zip(results, self.searchbutons): button.setText(emoji) button.pressed.connect(lambda ch=emoji: self.parent.make_preview(ch)) button.clicked.connect( lambda _, ch=emoji: QApplication.clipboard().setText(ch)) button.setToolTip("<center><h1>{0}<br>{1}".format( emoji, self.parent.get_description(emoji))) button.setDisabled(False) return results
class HistoryCompleter(QCompleter): """ Class implementing a completer for the browser history. """ def __init__(self, model, parent=None): """ Constructor @param model reference to the model (QAbstractItemModel) @param parent reference to the parent object (QObject) """ super(HistoryCompleter, self).__init__(model, parent) self.setPopup(HistoryCompletionView()) # Completion should be against the faked role. self.setCompletionRole(HistoryCompletionModel.HistoryCompletionRole) # Since the completion role is faked, advantage of the sorted-model # optimizations in QCompleter can be taken. self.setCaseSensitivity(Qt.CaseSensitive) self.setModelSorting(QCompleter.CaseSensitivelySortedModel) self.__searchString = "" self.__filterTimer = QTimer(self) self.__filterTimer.setSingleShot(True) self.__filterTimer.timeout.connect(self.__updateFilter) def pathFromIndex(self, idx): """ Public method to get a path for a given index. @param idx reference to the index (QModelIndex) @return the actual URL from the history (string) """ return self.model().data(idx, HistoryModel.UrlStringRole) def splitPath(self, path): """ Public method to split the given path into strings, that are used to match at each level in the model. @param path path to be split (string) @return list of path elements (list of strings) """ if path == self.__searchString: return ["t"] # Queue an update to the search string. Wait a bit, so that if the user # is quickly typing, the completer doesn't try to complete until they # pause. if self.__filterTimer.isActive(): self.__filterTimer.stop() self.__filterTimer.start(150) # If the previous search results are not a superset of the current # search results, tell the model that it is not valid yet. if not path.startswith(self.__searchString): self.model().setValid(False) self.__searchString = path # The actual filtering is done by the HistoryCompletionModel. Just # return a short dummy here so that QCompleter thinks everything # matched. return ["t"] def __updateFilter(self): """ Private slot to update the search string. """ completionModel = self.model() # Tell the HistoryCompletionModel about the new search string. completionModel.setSearchString(self.__searchString) # Sort the model. completionModel.sort(0) # Mark it valid. completionModel.setValid(True) # Now update the QCompleter widget, but only if the user is still # typing a URL. if self.widget() is not None and self.widget().hasFocus(): self.complete()
class Player(QMediaPlayer): signal_player_media_changed = pyqtSignal([SongModel]) signal_playlist_is_empty = pyqtSignal() signal_playback_mode_changed = pyqtSignal([PlaybackMode]) signal_playlist_finished = pyqtSignal() signal_song_required = pyqtSignal() finished = pyqtSignal() _music_list = list() # 里面的对象是music_model _current_index = None current_song = None _tmp_fix_next_song = None playback_mode = PlaybackMode.loop last_playback_mode = PlaybackMode.loop _other_mode = False def __init__(self, app): super().__init__(app) self._app = app self._stalled_timer = QTimer(self) self.error.connect(self.on_error_occured) self.mediaChanged.connect(self.on_media_changed) self.mediaStatusChanged.connect(self.on_media_status_changed) self._stalled_timer.timeout.connect(self._wait_to_seek_back) self._music_error_times = 0 self._retry_latency = 3 self._music_error_maximum = 3 self._media_stalled = False def change_player_mode_to_normal(self): logger.debug('退出特殊的播放模式') self._other_mode = False self._set_playback_mode(self.last_playback_mode) def change_player_mode_to_other(self): # player mode: such as fm mode, different from playback mode logger.debug('进入特殊的播放模式') self._other_mode = True self._set_playback_mode(PlaybackMode.sequential) def _record_playback_mode(self): self.last_playback_mode = self.playback_mode @pyqtSlot(QMediaContent) def on_media_changed(self, media_content): music_model = self._music_list[self._current_index] self.signal_player_media_changed.emit(music_model) @pyqtSlot(QMediaPlayer.MediaStatus) def on_media_status_changed(self, state): self._media_stalled = False if state == QMediaPlayer.EndOfMedia: self.finished.emit() self.stop() if (self._current_index == len(self._music_list) - 1) and\ self._other_mode: self.signal_playlist_finished.emit() logger.debug("播放列表播放完毕") if not self._other_mode: self.play_next() elif state in (QMediaPlayer.BufferedMedia, QMediaPlayer.LoadedMedia): self.play() elif state in (QMediaPlayer.StalledMedia, ): self.pause() self._media_stalled = True if not self._stalled_timer.isActive(): self._stalled_timer.start() self._stalled_timer.setInterval(3000) def insert_to_next(self, model): if not self.is_music_in_list(model): if self._current_index is None: index = 0 else: index = self._current_index + 1 self._music_list.insert(index, model) return True return False def add_music(self, song): self._music_list.append(song) def remove_music(self, mid): for i, music_model in enumerate(self._music_list): if mid == music_model.mid: self._music_list.pop(i) if self._current_index is not None: if i == self._current_index: self.play_next() elif i < self._current_index: self._current_index -= 1 return True return False def get_media_content_from_model(self, music_model): url = music_model.url if not url: self._app.message('URL 不存在,不能播放该歌曲') return None if url.startswith('http'): media_content = QMediaContent(QUrl(url)) else: media_content = QMediaContent(QUrl.fromLocalFile(url)) return media_content def set_music_list(self, music_list): self._music_list = [] self._music_list = music_list if len(self._music_list): self.play(self._music_list[0]) def clear_playlist(self): self._music_list = [] self._current_index = None self.current_song = None self.stop() def is_music_in_list(self, model): for music in self._music_list: if model.mid == music.mid: return True return False def _play(self, music_model): insert_flag = self.insert_to_next(music_model) index = self.get_index_by_model(music_model) if not insert_flag and self._current_index is not None: if music_model.mid == self.current_song.mid\ and self.state() == QMediaPlayer.PlayingState: return True super().stop() media_content = self.get_media_content_from_model(music_model) if media_content is not None: self._app.message('正在准备播放 %s' % music_model.title) logger.debug('start to play song: %d, %s, %s' % (music_model.mid, music_model.title, music_model.url)) self._current_index = index self.current_song = music_model self.setMedia(media_content) # super().play() return True else: self._app.message('%s 不能播放, 准备播放下一首' % music_model.title) self.remove_music(music_model.mid) self.play_next() return False def other_mode_play(self, music_model): self._play(music_model) def play(self, music_model=None): if music_model is None: super().play() return False self._app.message('准备播放 %s' % music_model.title) self._app.player_mode_manager.exit_to_normal() self._play(music_model) def get_index_by_model(self, music_model): for i, music in enumerate(self._music_list): if music_model.mid == music.mid: return i return None def play_or_pause(self): if len(self._music_list) is 0: self.signal_playlist_is_empty.emit() return if self.state() == QMediaPlayer.PlayingState: self.pause() elif self.state() == QMediaPlayer.PausedState: self.play() else: self.play_next() def play_next(self): if self._tmp_fix_next_song is not None: flag = self.play(self._tmp_fix_next_song) self._tmp_fix_next_song = None return flag index = self.get_next_song_index() if index is not None: if index == 0 and self._other_mode: self.signal_playlist_finished.emit() logger.debug("播放列表播放完毕") return music_model = self._music_list[index] self.play(music_model) return True else: self.signal_playlist_is_empty.emit() return False def play_last(self): index = self.get_previous_song_index() if index is not None: music_model = self._music_list[index] self.play(music_model) return True else: self.signal_playlist_is_empty.emit() return False def set_tmp_fixed_next_song(self, song): self._tmp_fix_next_song = song @pyqtSlot(QMediaPlayer.Error) def on_error_occured(self, error): song = self._music_list[self._current_index] self._app.message('%s 不能播放' % (song.title)) self.stop() if error == QMediaPlayer.FormatError: self._app.message('这首歌挂了,也有可能是断网了', error=True) logger.debug('song cant be played, url is %s' % song.url) elif error == QMediaPlayer.NetworkError: self._wait_to_retry() elif error == QMediaPlayer.ResourceError: logger.error('网络出现错误:不能正确解析资源') elif error == QMediaPlayer.ServiceMissingError: self._app.notify('缺少解码器,请向作者求助', error=True) else: self._wait_to_next(2) def _wait_to_retry(self): if self._music_error_times >= self._music_error_maximum: self._music_error_times = 0 self._wait_to_next(self._retry_latency) self._app.message('将要播放下一首歌曲', error=True) else: self._music_error_times += 1 app_event_loop = asyncio.get_event_loop() app_event_loop.call_later(self._retry_latency, self.play) self._app.message('网络连接不佳', error=True) def _wait_to_seek_back(self): if not self._media_stalled: return self._app.message('播放器将退回到 1s 之前的位置') self._media_stalled = False self._stalled_timer.stop() current_position = self.position() two_second_back = current_position - 1 * 1000 if two_second_back < 0: two_second_back = 0 self.setPosition(two_second_back) logger.warning('seek back media because current media is stalled') logger.warning('curr position is %d:%d' % parse_ms(current_position)) logger.warning('back position is %d:%d' % parse_ms(two_second_back)) def _wait_to_next(self, second=0): if len(self._music_list) < 2: return app_event_loop = asyncio.get_event_loop() app_event_loop.call_later(second, self.play_next) def get_next_song_index(self): if len(self._music_list) is 0: self._app.message('当前播放列表没有歌曲') return None if self.playback_mode == PlaybackMode.one_loop: return self._current_index elif self.playback_mode == PlaybackMode.loop: if self._current_index >= len(self._music_list) - 1: return 0 else: return self._current_index + 1 elif self.playback_mode == PlaybackMode.sequential: self.signal_playlist_finished.emit() return None else: return random.choice(range(len(self._music_list))) def get_previous_song_index(self): if len(self._music_list) is 0: return None if self.playback_mode == PlaybackMode.one_loop: return self._current_index elif self.playback_mode == PlaybackMode.loop: if self._current_index is 0: return len(self._music_list) - 1 else: return self._current_index - 1 elif self.playback_mode == PlaybackMode.sequential: return None else: return random.choice(range(len(self._music_list))) def _set_playback_mode(self, mode): # item once: 0 # item in loop: 1 # sequential: 2 # loop: 3 # random: 4 if mode == self.playback_mode: return 0 self._record_playback_mode() self.playback_mode = mode self._app.message('设置播放顺序为:%s' % mode.value) self.signal_playback_mode_changed.emit(mode) def next_playback_mode(self): if self.playback_mode == PlaybackMode.one_loop: self._set_playback_mode(PlaybackMode.loop) elif self.playback_mode == PlaybackMode.loop: self._set_playback_mode(PlaybackMode.random) elif self.playback_mode == PlaybackMode.random: self._set_playback_mode(PlaybackMode.one_loop) @property def songs(self): return self._music_list
class GenericOutputController(PrinterOutputController): def __init__(self, output_device: "PrinterOutputDevice") -> None: super().__init__(output_device) self._preheat_bed_timer = QTimer() self._preheat_bed_timer.setSingleShot(True) self._preheat_bed_timer.timeout.connect(self._onPreheatBedTimerFinished) self._preheat_printer = None # type: Optional[PrinterOutputModel] self._preheat_hotends_timer = QTimer() self._preheat_hotends_timer.setSingleShot(True) self._preheat_hotends_timer.timeout.connect(self._onPreheatHotendsTimerFinished) self._preheat_hotends = set() # type: Set[ExtruderOutputModel] self._output_device.printersChanged.connect(self._onPrintersChanged) self._active_printer = None # type: Optional[PrinterOutputModel] def _onPrintersChanged(self) -> None: if self._active_printer: self._active_printer.stateChanged.disconnect(self._onPrinterStateChanged) self._active_printer.targetBedTemperatureChanged.disconnect(self._onTargetBedTemperatureChanged) for extruder in self._active_printer.extruders: extruder.targetHotendTemperatureChanged.disconnect(self._onTargetHotendTemperatureChanged) self._active_printer = self._output_device.activePrinter if self._active_printer: self._active_printer.stateChanged.connect(self._onPrinterStateChanged) self._active_printer.targetBedTemperatureChanged.connect(self._onTargetBedTemperatureChanged) for extruder in self._active_printer.extruders: extruder.targetHotendTemperatureChanged.connect(self._onTargetHotendTemperatureChanged) def _onPrinterStateChanged(self) -> None: if self._active_printer and self._active_printer.state != "idle": if self._preheat_bed_timer.isActive(): self._preheat_bed_timer.stop() if self._preheat_printer: self._preheat_printer.updateIsPreheating(False) if self._preheat_hotends_timer.isActive(): self._preheat_hotends_timer.stop() for extruder in self._preheat_hotends: extruder.updateIsPreheating(False) self._preheat_hotends = set() # type: Set[ExtruderOutputModel] def moveHead(self, printer: "PrinterOutputModel", x, y, z, speed) -> None: self._output_device.sendCommand("G91") self._output_device.sendCommand("G0 X%s Y%s Z%s F%s" % (x, y, z, speed)) self._output_device.sendCommand("G90") def homeHead(self, printer: "PrinterOutputModel") -> None: self._output_device.sendCommand("G28 X Y") def homeBed(self, printer: "PrinterOutputModel") -> None: self._output_device.sendCommand("G28 Z") def sendRawCommand(self, printer: "PrinterOutputModel", command: str) -> None: self._output_device.sendCommand(command.upper()) #Most printers only understand uppercase g-code commands. def setJobState(self, job: "PrintJobOutputModel", state: str) -> None: if state == "pause": self._output_device.pausePrint() job.updateState("paused") elif state == "print": self._output_device.resumePrint() job.updateState("printing") elif state == "abort": self._output_device.cancelPrint() pass def setTargetBedTemperature(self, printer: "PrinterOutputModel", temperature: int) -> None: self._output_device.sendCommand("M140 S%s" % temperature) def _onTargetBedTemperatureChanged(self) -> None: if self._preheat_bed_timer.isActive() and self._preheat_printer and self._preheat_printer.targetBedTemperature == 0: self._preheat_bed_timer.stop() self._preheat_printer.updateIsPreheating(False) def preheatBed(self, printer: "PrinterOutputModel", temperature, duration) -> None: try: temperature = round(temperature) # The API doesn't allow floating point. duration = round(duration) except ValueError: return # Got invalid values, can't pre-heat. self.setTargetBedTemperature(printer, temperature=temperature) self._preheat_bed_timer.setInterval(duration * 1000) self._preheat_bed_timer.start() self._preheat_printer = printer printer.updateIsPreheating(True) def cancelPreheatBed(self, printer: "PrinterOutputModel") -> None: self.setTargetBedTemperature(printer, temperature=0) self._preheat_bed_timer.stop() printer.updateIsPreheating(False) def _onPreheatBedTimerFinished(self) -> None: if not self._preheat_printer: return self.setTargetBedTemperature(self._preheat_printer, 0) self._preheat_printer.updateIsPreheating(False) def setTargetHotendTemperature(self, printer: "PrinterOutputModel", position: int, temperature: Union[int, float]) -> None: self._output_device.sendCommand("M104 S%s T%s" % (temperature, position)) def _onTargetHotendTemperatureChanged(self) -> None: if not self._preheat_hotends_timer.isActive(): return if not self._active_printer: return for extruder in self._active_printer.extruders: if extruder in self._preheat_hotends and extruder.targetHotendTemperature == 0: extruder.updateIsPreheating(False) self._preheat_hotends.remove(extruder) if not self._preheat_hotends: self._preheat_hotends_timer.stop() def preheatHotend(self, extruder: "ExtruderOutputModel", temperature, duration) -> None: position = extruder.getPosition() number_of_extruders = len(extruder.getPrinter().extruders) if position >= number_of_extruders: return # Got invalid extruder nr, can't pre-heat. try: temperature = round(temperature) # The API doesn't allow floating point. duration = round(duration) except ValueError: return # Got invalid values, can't pre-heat. self.setTargetHotendTemperature(extruder.getPrinter(), position, temperature=temperature) self._preheat_hotends_timer.setInterval(duration * 1000) self._preheat_hotends_timer.start() self._preheat_hotends.add(extruder) extruder.updateIsPreheating(True) def cancelPreheatHotend(self, extruder: "ExtruderOutputModel") -> None: self.setTargetHotendTemperature(extruder.getPrinter(), extruder.getPosition(), temperature=0) if extruder in self._preheat_hotends: extruder.updateIsPreheating(False) self._preheat_hotends.remove(extruder) if not self._preheat_hotends and self._preheat_hotends_timer.isActive(): self._preheat_hotends_timer.stop() def _onPreheatHotendsTimerFinished(self) -> None: for extruder in self._preheat_hotends: self.setTargetHotendTemperature(extruder.getPrinter(), extruder.getPosition(), 0) self._preheat_hotends = set() #type: Set[ExtruderOutputModel] # Cancel any ongoing preheating timers, without setting back the temperature to 0 # This can be used eg at the start of a print def stopPreheatTimers(self) -> None: if self._preheat_hotends_timer.isActive(): for extruder in self._preheat_hotends: extruder.updateIsPreheating(False) self._preheat_hotends = set() #type: Set[ExtruderOutputModel] self._preheat_hotends_timer.stop() if self._preheat_bed_timer.isActive(): if self._preheat_printer: self._preheat_printer.updateIsPreheating(False) self._preheat_bed_timer.stop()
class MarkerFolder(QObject, HasWeakEditorMixin): markerStart = re.compile(r'\{\{\{') markerEnd = re.compile(r'\}\}\}') interval = 100 def __init__(self, editor=None, **kwargs): super(MarkerFolder, self).__init__(**kwargs) self.editor = editor editor.sciModified.connect(self.onModification) self.timer = QTimer() self.timer.setSingleShot(True) self.timer.timeout.connect(self.refoldQueue) self.linesToRefold = set() if editor: self.refold(True) @Slot() def refold(self, force=False): self.refoldAt(0, force) @Slot() def refoldQueue(self, force=False): while len(self.linesToRefold): start = self.linesToRefold.pop() self.refoldAt(start, force) def refoldAt(self, start, force=False): waitnext = True level = self.editor.getFoldLevel(start) & QsciScintilla.SC_FOLDLEVELNUMBERMASK for i in range(start, self.editor.lines()): self.linesToRefold.discard(i) flag = 0 line = self.editor.text(i) diff = len(self.markerStart.findall(line)) if diff: flag |= QsciScintilla.SC_FOLDLEVELHEADERFLAG diff -= len(self.markerEnd.findall(line)) new = level | flag current = self.editor.getFoldLevel(i) if force or current != new: self.editor.setFoldLevel(i, new) waitnext = True else: if not waitnext: break waitnext = False level += diff @Slot(object) def onModification(self, st): refold = None if st.modificationType & QsciScintilla.SC_MOD_INSERTTEXT: refold, _ = self.editor.lineIndexFromPosition(st.position) elif st.modificationType & QsciScintilla.SC_MOD_DELETETEXT: refold, _ = self.editor.lineIndexFromPosition(st.position) if refold is not None: self.linesToRefold.add(refold) if not self.timer.isActive(): self.timer.start(self.interval)
class MyWidget(QWidget): def __init__(self, parent): super().__init__(parent) self.initUI() def initUI(self): self.setMinimumSize(1, 30) self.setFocusPolicy(Qt.StrongFocus) # to be able to accept key events self.paint_image = QPixmap(self.width(), self.height()) self.background_color = QColor(70, 70, 70) self.fps = 30 self.timer = QTimer() self.timer.timeout.connect(self.nextFrame) self.timer.start(1000 / 30) self.frame_count = 0 self.x1 = 50 self.y1 = 50 self.radius1 = 50 self.color2 = QColor(0, 0, 255) self.x2 = 50 self.y2 = 50 self.angle2 = 20 def setFrameRate(self, fps): self.fps = fps if self.fps > 0: if not self.timer.isActive(): self.timer.start() self.timer.setInterval(1000 / self.fps) else: self.timer.stop() def nextFrame(self): mouse_pos = self.mapFromGlobal(QCursor.pos()) mouse_x = mouse_pos.x() mouse_y = mouse_pos.y() self.x1 = mouse_x self.y1 = mouse_y self.radius1 = self.radius1 + math.sin(self.frame_count / 8) * 4 delay = 20 self.x2 += (mouse_x - self.x2) / delay self.y2 += (mouse_y - self.y2) / delay self.frame_count += 1 if self.paint_image.width() != self.width() or self.paint_image.height() != self.height(): self.paint_image = QPixmap(self.width(), self.height()) self.paint_image.fill(self.background_color) p = QPainter() p.begin(self.paint_image) # auf das paint_image malen p.setBackground(self.background_color) # color when stuff is erased # Hintergrund löschen, wenn Rechteck bei der Maus angekommen ist # print("{0}, {1}".format(self.x2, mouse_x)) if round(self.x2) == mouse_x and round(self.y2) == mouse_y: p.eraseRect(0, 0, self.paint_image.width(), self.paint_image.height()) self.drawFrame(p) p.end() self.repaint() def drawFrame(self, p): pen = QPen(QColor(20, 20, 20), 10, Qt.SolidLine) p.setPen(pen) x1 = self.x1 - self.radius1 / 2 y1 = self.y1 - self.radius1 / 2 p.drawEllipse(x1, y1, self.radius1, self.radius1) pen = QPen(self.color2, 5, Qt.SolidLine) # blau p.setPen(pen) w2 = 50 # see https://www.khanacademy.org/computing/computer-programming/programming-games-visualizations/programming-transformations/a/rotation p.save() p.translate(self.x2, self.y2) p.rotate(self.angle2) # todo keypress p.drawRect(0, 0, w2, w2) p.restore() def paintEvent(self, e): p = QPainter() p.begin(self) # self.drawFrame(p) # Frame direkt malen (ohne paint_image) p.drawPixmap(0, 0, self.paint_image) p.end() def keyPressEvent(self, e): print(e.key(), e.text()) if e.key() == Qt.Key_Left: self.angle2 -= 10 elif e.key() == Qt.Key_Right: self.angle2 += 10 if e.key() == Qt.Key_P: print("P pressed") if e.key() == Qt.Key_C: print("C pressed") if self.color2 == QColor(0, 0, 255): self.color2 = QColor(0, 255, 0) else: self.color2 = QColor(0, 0, 255) def mousePressEvent(self, e): print(e.x(), e.y()) p = QPainter() p.begin(self.paint_image) # auf das paint_image malen self.drawOnClick(e, p) p.end() self.repaint() def drawOnClick(self, e, p): pen = QPen(QColor(255, 0, 0), 20, Qt.SolidLine, Qt.RoundCap) p.setPen(pen) p.drawPoint(e.x(), e.y())
class View(KineticScrollArea): MAX_ZOOM = 4.0 viewModeChanged = pyqtSignal(int) def __init__(self, parent=None): super(View, self).__init__(parent) self.setAlignment(Qt.AlignCenter) self.setBackgroundRole(QPalette.Dark) self.setMouseTracking(True) self._viewMode = FixedScale self._wheelZoomEnabled = True self._wheelZoomModifier = Qt.CTRL self._pinchStartFactor = None super(View, self).grabGesture(Qt.PinchGesture) # delayed resize self._centerPos = False self._resizeTimer = QTimer(singleShot = True, timeout = self._resizeTimeout) def surface(self): """Returns our Surface, the widget drawing the page(s).""" sf = self.widget() if not sf: sf = surface.Surface(self) self.setSurface(sf) return sf def setSurface(self, sf): """Sets the given surface as our widget.""" self.setWidget(sf) # For some reason mouse tracking *must* be enabled on the child as well... sf.setMouseTracking(True) self.kineticScrollingActive.connect(sf.updateKineticCursor) def viewMode(self): """Returns the current ViewMode.""" return self._viewMode def setViewMode(self, mode): """Sets the current ViewMode.""" if mode == self._viewMode: return self._viewMode = mode if mode: self.fit() self.viewModeChanged.emit(mode) def wheelZoomEnabled(self): """Returns whether wheel zoom is enabled.""" return self._wheelZoomEnabled def setWheelZoomEnabled(self, enabled): """Sets whether wheel zoom is enabled. Wheel zoom is zooming using the mouse wheel and a keyboard modifier key (defaulting to Qt.CTRL). Use setWheelZoomModifier() to set a key (or key combination). """ self._wheelZoomEnabled = enabled def wheelZoomModifier(self): """Returns the modifier key to wheel-zoom with (defaults to Qt.CTRL).""" return self._wheelZoomModifier def setWheelZoomModifier(self, key): """Sets the modifier key to wheel-zoom with (defaults to Qt.CTRL). Can also be set to a ORed value, e.g. Qt.SHIFT|Qt.ALT. Only use Qt.ALT, Qt.CTRL, Qt.SHIFT and/or Qt.META. """ self._wheelZoomModifier = key def load(self, document): """Convenience method to load all the pages from the given Poppler.Document.""" self.surface().pageLayout().load(document) # don't do a fit() before the very first resize as the size is then bogus if self.viewMode(): self.fit() self.surface().pageLayout().update() def clear(self): """Convenience method to clear the current layout.""" self.surface().pageLayout().clear() self.surface().pageLayout().update() def scale(self): """Returns the scale of the pages in the View.""" return self.surface().pageLayout().scale() def setScale(self, scale): """Sets the scale of all pages in the View.""" self.surface().pageLayout().setScale(scale) self.surface().pageLayout().update() self.setViewMode(FixedScale) def visiblePages(self): """Yields the visible pages.""" rect = self.viewport().rect() rect.translate(-self.surface().pos()) rect &= self.surface().rect() return self.surface().pageLayout().pagesAt(rect) def redraw(self): """Redraws, e.g. when you changed rendering hints or papercolor on the document.""" pages = list(self.visiblePages()) documents = set(page.document() for page in pages) for document in documents: cache.clear(document) for page in pages: page.repaint() def fit(self): """(Internal). Fits the layout according to the view mode. Prevents scrollbar/resize loops by precalculating which scrollbars will appear. """ mode = self.viewMode() if mode == FixedScale: return maxsize = self.maximumViewportSize() # can vertical or horizontal scrollbars appear? vcan = self.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded hcan = self.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded # width a scrollbar takes off the viewport size framewidth = 0 if self.style().styleHint(QStyle.SH_ScrollView_FrameOnlyAroundContents, None, self): framewidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth) * 2 scrollbarextent = self.style().pixelMetric(QStyle.PM_ScrollBarExtent, None, self) + framewidth # first try to fit full size layout = self.surface().pageLayout() layout.fit(maxsize, mode) layout.reLayout() # minimal values minwidth = maxsize.width() minheight = maxsize.height() if vcan: minwidth -= scrollbarextent if hcan: minheight -= scrollbarextent # do width and/or height fit? fitw = layout.width() <= maxsize.width() fith = layout.height() <= maxsize.height() if not fitw and not fith: if vcan or hcan: layout.fit(QSize(minwidth, minheight), mode) elif mode & FitWidth and fitw and not fith and vcan: # a vertical scrollbar will appear w = minwidth layout.fit(QSize(w, maxsize.height()), mode) layout.reLayout() if layout.height() <= maxsize.height(): # now the vert. scrollbar would disappear! # enlarge it as long as the vertical scrollbar would not be needed while True: w += 1 layout.fit(QSize(w, maxsize.height()), mode) layout.reLayout() if layout.height() > maxsize.height(): layout.fit(QSize(w - 1, maxsize.height()), mode) break elif mode & FitHeight and fith and not fitw and hcan: # a horizontal scrollbar will appear h = minheight layout.fit(QSize(maxsize.width(), h), mode) layout.reLayout() if layout.width() <= maxsize.width(): # now the horizontal scrollbar would disappear! # enlarge it as long as the horizontal scrollbar would not be needed while True: h += 1 layout.fit(QSize(maxsize.width(), h), mode) layout.reLayout() if layout.width() > maxsize.width(): layout.fit(QSize(maxsize.width(), h - 1), mode) break layout.update() def resizeEvent(self, ev): super(View, self).resizeEvent(ev) # Adjust the size of the document if desired if self.viewMode() and any(self.surface().pageLayout().pages()): if self._centerPos is False: self._centerPos = QPoint(0, 0) elif self._centerPos is None: # store the point currently in the center self._centerPos = self.viewport().rect().center() - self.surface().pos() if not self._resizeTimer.isActive(): self._resizeTimeout() self._resizeTimer.start(150) def _resizeTimeout(self): if self._centerPos is None: return oldSize = self.surface().size() # resize the layout self.fit() # restore our position newSize = self.surface().size() newx = self._centerPos.x() * newSize.width() // oldSize.width() newy = self._centerPos.y() * newSize.height() // oldSize.height() # we explicitly want the non-kinetic centering function regardless of kinetic state. self.fastCenter(QPoint(newx, newy)) self._centerPos = None def zoom(self, scale, pos=None): """Changes the display scale (1.0 is 100%). If pos is given, keeps that point at the same place if possible. Pos is a QPoint relative to ourselves. """ scale = max(0.05, min(self.MAX_ZOOM, scale)) if scale == self.scale(): return if self.surface().pageLayout().count() == 0: self.setScale(scale) return if pos is None: pos = self.viewport().rect().center() surfacePos = pos - self.surface().pos() page = self.surface().pageLayout().pageAt(surfacePos) if page: pagePos = surfacePos - page.pos() x = pagePos.x() / float(page.width()) y = pagePos.y() / float(page.height()) self.setScale(scale) newPos = QPoint(round(x * page.width()), round(y * page.height())) + page.pos() else: x = surfacePos.x() / float(self.surface().width()) y = surfacePos.y() / float(self.surface().height()) self.setScale(scale) newPos = QPoint(round(x * self.surface().width()), round(y * self.surface().height())) surfacePos = pos - self.surface().pos() # use fastScrollBy as we do not want kinetic scrolling here regardless of its state. self.fastScrollBy(newPos - surfacePos) def zoomIn(self, pos=None, factor=1.1): self.zoom(self.scale() * factor, pos) def zoomOut(self, pos=None, factor=1.1): self.zoom(self.scale() / factor, pos) def wheelEvent(self, ev): if (self._wheelZoomEnabled and int(ev.modifiers()) & _SCAM == self._wheelZoomModifier): factor = 1.1 ** (ev.angleDelta().y() / 120) if ev.angleDelta().y(): self.zoom(self.scale() * factor, ev.pos()) else: super(View, self).wheelEvent(ev) def mousePressEvent(self, ev): """Mouse press event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if not self.surface().handleMousePressEvent(ev): super(View, self).mousePressEvent(ev) def mouseReleaseEvent(self, ev): """Mouse release event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if not self.surface().handleMouseReleaseEvent(ev): super(View, self).mouseReleaseEvent(ev) def mouseMoveEvent(self, ev): """Mouse move event handler. Passes the event to the surface, and back to the base class if the surface did not do anything with it.""" if self.kineticIsIdle(): if self.surface().handleMouseMoveEvent(ev): return super(View, self).mouseMoveEvent(ev) def moveEvent(self, ev): """Move event handler. Passes the event to the surface if we've not started any kinetic move, and back to the base class if the surface did not do anything with it.""" if self.kineticIsIdle(): if self.surface().handleMoveEvent(ev): return super(View, self).moveEvent(ev) def event(self, ev): if isinstance(ev, QHelpEvent): if self.surface().handleHelpEvent(ev): ev.accept() return True if isinstance(ev, QGestureEvent): if self.gestureEvent(ev): ev.accept() # Accepts all gestures in the event return True return super(View, self).event(ev) def gestureEvent(self, event): """Gesture event handler. Return False if event is not accepted. Currently only cares about PinchGesture. Could also handle Swipe and Pan gestures.""" pinch = event.gesture(Qt.PinchGesture) if pinch: return self.pinchGesture(pinch) return False def pinchGesture( self, gesture ): """Pinch gesture event handler. Return False if event is not accepted. Currently only cares about ScaleFactorChanged and not RotationAngleChanged.""" # Gesture start? Reset _pinchStartFactor in case we didn't # catch the finish event if gesture.state() == Qt.GestureStarted: self._pinchStartFactor = None changeFlags = gesture.changeFlags() if changeFlags & QPinchGesture.ScaleFactorChanged: factor = gesture.property("scaleFactor") if not self._pinchStartFactor: # Gesture start? self._pinchStartFactor = self.scale() self.zoom(self._pinchStartFactor * factor, self.mapFromGlobal(gesture.hotSpot().toPoint()) ) # Gesture finished? if gesture.state() in (Qt.GestureFinished, Qt.GestureCanceled): self._pinchStartFactor = None return True def currentPage(self): """Returns the Page currently mostly in the center, or None if there are no pages.""" pos = self.viewport().rect().center() - self.surface().pos() layout = self.surface().pageLayout() if len(layout): d = layout.spacing() * 2 for dx, dy in ((0, 0), (-d, 0), (0, -d), (d, 0), (0, d)): dist = QPoint(dx, dy) page = layout.pageAt(pos + dist) if page: return page def currentPageNumber(self): """Returns the number (index in the layout) of the currentPage(), or -1 if there are no pages.""" page = self.currentPage() if page: return self.surface().pageLayout().index(page) return -1 def gotoPageNumber(self, num): """Aligns the page at the given index in the layout to the topleft of our View.""" layout = self.surface().pageLayout() if num < len(layout) and num != self.currentPageNumber(): margin = QPoint(layout.margin(), layout.margin()) self.scrollBy(layout[num].pos() + self.surface().pos() - margin) def position(self): """Returns a three-tuple(num, x, y) describing the page currently in the center of the View. the number is the index of the Page in the Layout, and x and y are the coordinates in the range 0.0 -> 1.0 of the point that is at the center of the View. This way a position can be retained even if the scale or the orientation of the Layout changed. Returns None, None, None if the layout is empty. """ page = self.currentPage() if page: layout = self.surface().pageLayout() pos = self.viewport().rect().center() - self.surface().pos() pagePos = pos - page.pos() x = pagePos.x() / float(page.width()) y = pagePos.y() / float(page.height()) return layout.index(page), x, y return None, None, None def setPosition(self, position, overrideKinetic=False): """Sets the position to a three-tuple as previously returned by position(). Setting overrideKinetic to true allows for fast setup, instead of scrolling all the way to the visible point. """ layout = self.surface().pageLayout() pageNum, x, y = position if pageNum is None or pageNum >= len(layout): return page = layout[pageNum] # center this point newPos = QPoint(round(x * page.width()), round(y * page.height())) + page.pos() if overrideKinetic: self.fastCenter(newPos) else: self.center(newPos)