Beispiel #1
0
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()
Beispiel #2
0
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()
Beispiel #3
0
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()
Beispiel #6
0
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)
Beispiel #7
0
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
Beispiel #8
0
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 = {}
Beispiel #10
0
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)
Beispiel #11
0
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)
Beispiel #13
0
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()
Beispiel #14
0
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()
Beispiel #15
0
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))
Beispiel #16
0
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))
Beispiel #17
0
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 = []
Beispiel #18
0
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
Beispiel #19
0
    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)
Beispiel #21
0
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()
Beispiel #22
0
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))
Beispiel #23
0
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)
Beispiel #24
0
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))
Beispiel #25
0
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()
Beispiel #26
0
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)
Beispiel #27
0
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)
Beispiel #28
0
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()
Beispiel #29
0
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()
Beispiel #31
0
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
Beispiel #32
0
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()
Beispiel #33
0
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
Beispiel #34
0
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)
Beispiel #35
0
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
Beispiel #36
0
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()
Beispiel #37
0
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()
Beispiel #38
0
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()
Beispiel #39
0
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()
Beispiel #40
0
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()
Beispiel #41
0
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()]
Beispiel #42
0
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()
Beispiel #44
0
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')
Beispiel #47
0
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 = ""
Beispiel #48
0
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)
Beispiel #49
0
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
Beispiel #50
0
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))
Beispiel #51
0
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
Beispiel #52
0
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()
Beispiel #53
0
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
Beispiel #55
0
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()
Beispiel #56
0
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()
Beispiel #58
0
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())
Beispiel #60
0
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)