Beispiel #1
0
class DirectoryWidget(QToolButton):
    Sg_double_clicked = Signal(str)

    def __init__(self):
        super(DirectoryWidget, self).__init__()
        self.timer = QTimer()
        self.timer.setSingleShot(True)
        self.clicked.connect(self.Sl_check_double_click)

        self.setAccessibleName('Directory')

        self.setIcon(QIcon(resource_path("icons/Cartella.png")))
        self.setIconSize(QSize(45, 45))
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)

    @Slot()
    def Sl_check_double_click(self):
        success = False
        if self.timer.isActive():
            time = self.timer.remainingTime()
            if time > 0:
                self.double_clicked_action()
                success = True
                self.timer.stop()
            if time <= 0:
                self.timer.start(250)

        if not self.timer.isActive() and not success:
            self.timer.start(250)

    def double_clicked_action(self) -> None:
        pass
Beispiel #2
0
class FileWidget(QToolButton):
    Sg_double_clicked = Signal()

    def __init__(self, file: File):
        super(FileWidget, self).__init__()

        self.timer = QTimer()
        self.timer.setSingleShot(True)

        self.clicked.connect(self.check_double_click)
        self.Sg_double_clicked.connect(self.Sl_on_double_click)

        self.setAccessibleName('File')

        self.name = file.get_name()
        self.creation_date = file.get_creation_date()
        self.last_modified_date = file.get_last_modified_date()

        self.extension = self.get_extension()

        self.set_icon()
        self.setText(self.name)

    def get_extension(self) -> str:
        if self.name.find('.') != -1:
            e = self.name.split(".")
            return e[-1]
        else:
            return "no"

    def set_icon(self):
        self.setIconSize(QSize(45, 45))
        self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
        if self.extension in ["txt", "xml", "json", "docx", "xlsx"]:
            self.setIcon(QIcon(resource_path('icons/Txt.png')))
        elif self.extension in ["mp4", "avi", "mpeg", "wmv"]:
            self.setIcon(QIcon(resource_path('icons/Video.png')))
        elif self.extension in ["jpg", "png", "gif"]:
            self.setIcon(QIcon(resource_path('icons/Immagine.png')))
        elif self.extension in ["mp3", "wav", "ogg"]:
            self.setIcon(QIcon(resource_path('icons/Audio.png')))
        else:
            self.setIcon(QIcon(resource_path('icons/DocGenerico.png')))

    def check_double_click(self):
        if self.timer.isActive():
            time = self.timer.remainingTime()
            if time > 0:
                self.Sg_double_clicked.emit()
            self.timer.stop()
            if time <= 0:
                self.timer.start(250)

        if self.timer.isActive() is False:
            self.timer.start(250)

    @Slot()
    def Sl_on_double_click(self):
        pass
Beispiel #3
0
class WinForm(QWidget):
    def __init__(self, parent=None):
        super(WinForm, self).__init__(parent)
        self.setWindowTitle("QTimer demo")
        self.listFile = QListWidget()
        self.label = QLabel('显示当前时间')
        self.startBtn = QPushButton('开始')
        self.endBtn = QPushButton('结束')
        layout = QGridLayout(self)

        # 初始化一个定时器
        self.timer = QTimer(self)
        # showTime()方法
        self.timer.timeout.connect(self.showTime)

        layout.addWidget(self.label, 0, 0, 1, 2)
        layout.addWidget(self.startBtn, 1, 0)
        layout.addWidget(self.endBtn, 1, 1)

        self.startBtn.clicked.connect(self.startTimer)
        self.endBtn.clicked.connect(self.endTimer)

        self.setLayout(layout)

    def showTime(self):
        # 获取系统现在的时间
        time = QDateTime.currentDateTime()
        # 设置系统时间显示格式
        timeDisplay = time.toString("yyyy-MM-dd hh:mm:ss dddd")
        # 在标签上显示时间
        self.label.setText(timeDisplay)

    def startTimer(self):
        # 设置计时间隔并启动
        self.timer.start(1000)
        self.startBtn.setEnabled(False)
        self.endBtn.setEnabled(True)

    def endTimer(self):
        self.timer.stop()
        self.startBtn.setEnabled(True)
        self.endBtn.setEnabled(False)
class TimerQT(TimerBase):
    def __init__(self, *args, **kwargs):
        self._timer = QTimer()
        self._timer.timeout.connect(self._on_timer)
        TimerBase.__init__(self, *args, **kwargs)

    def __del__(self):
        self._timer_stop()

    def _timer_set_single_shot(self):
        self._timer.setSingleShot(self._single)

    def _timer_set_interval(self):
        self._timer.setInterval(self._interval)

    def _timer_start(self):
        self._timer.start()

    def _timer_stop(self):
        self._timer.stop()
class Widget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.setMinimumSize(800, 600)
        self.donuts = []
        self.chart_view = QChartView()
        self.chart_view.setRenderHint(QPainter.Antialiasing)
        self.chart = self.chart_view.chart()
        self.chart.legend().setVisible(False)
        self.chart.setTitle("Nested donuts demo")
        self.chart.setAnimationOptions(QChart.AllAnimations)

        self.min_size = 0.1
        self.max_size = 0.9
        self.donut_count = 5

        self.setup_donuts()

        # create main layout
        self.main_layout = QGridLayout(self)
        self.main_layout.addWidget(self.chart_view, 1, 1)
        self.setLayout(self.main_layout)

        self.update_timer = QTimer(self)
        self.update_timer.timeout.connect(self.update_rotation)
        self.update_timer.start(1250)

    def setup_donuts(self):
        for i in range(self.donut_count):
            donut = QPieSeries()
            slccount = randrange(3, 6)
            for j in range(slccount):
                value = randrange(100, 200)

                slc = QPieSlice(str(value), value)
                slc.setLabelVisible(True)
                slc.setLabelColor(Qt.white)
                slc.setLabelPosition(QPieSlice.LabelInsideTangential)

                # Connection using an extra parameter for the slot
                slc.hovered[bool].connect(partial(self.explode_slice, slc=slc))

                donut.append(slc)
                size = (self.max_size - self.min_size) / self.donut_count
                donut.setHoleSize(self.min_size + i * size)
                donut.setPieSize(self.min_size + (i + 1) * size)

            self.donuts.append(donut)
            self.chart_view.chart().addSeries(donut)

    def update_rotation(self):
        for donut in self.donuts:
            phase_shift = randrange(-50, 100)
            donut.setPieStartAngle(donut.pieStartAngle() + phase_shift)
            donut.setPieEndAngle(donut.pieEndAngle() + phase_shift)

    def explode_slice(self, exploded, slc):
        if exploded:
            self.update_timer.stop()
            slice_startangle = slc.startAngle()
            slice_endangle = slc.startAngle() + slc.angleSpan()

            donut = slc.series()
            idx = self.donuts.index(donut)
            for i in range(idx + 1, len(self.donuts)):
                self.donuts[i].setPieStartAngle(slice_endangle)
                self.donuts[i].setPieEndAngle(360 + slice_startangle)
        else:
            for donut in self.donuts:
                donut.setPieStartAngle(0)
                donut.setPieEndAngle(360)

            self.update_timer.start()

        slc.setExploded(exploded)
Beispiel #6
0
class AlarmClock(QWidget):

    started = Signal()
    aborted = Signal()
    finished = Signal()

    def __init__(self, parent=None):

        super().__init__(parent)
        self.alarm = QTime()
        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.update_timer = QTimer(self)
        self.update_timer.setInterval(1000)

        self.current_label = QLabel('00:00:00')
        font = QFont()
        font.setPointSize(50)
        self.current_label.setFont(font)
        self.time_edit = QTimeEdit(self)
        self.time_edit.setDisplayFormat('H:mm')
        self.set_around_btn = QPushButton('Set around time', self)

        self.set_ui()
        self.set_connection()
        self.reset()

        self.update_timer.start()
        self.update_label()
        self.set_around_time()

    def set_ui(self):
        self.vlayout = QVBoxLayout(self)
        self.vlayout.addWidget(self.current_label)
        self.vlayout.addWidget(self.time_edit)
        self.vlayout.addWidget(self.set_around_btn)

    def set_connection(self):
        self.timer.timeout.connect(self.stop)
        self.update_timer.timeout.connect(self.update_label)
        self.set_around_btn.clicked.connect(self.set_around_time)

    def set_around_time(self):
        current = QTime.currentTime()
        if current.minute() < 29:
            self.time_edit.setTime(QTime(current.hour(), 30))
        else:
            self.time_edit.setTime(QTime(current.hour() + 1, 0))

    def update_label(self):
        current = QTime.currentTime()
        self.current_label.setText('{hour:02}:{min:02}:{sec:02}'.format(
            hour=current.hour(), min=current.minute(), sec=current.second()))

    def start(self):
        self.time_edit.setEnabled(False)
        self.alarm = self.time_edit.time()
        self.timer.start(QTime.currentTime().msecsTo(self.alarm))
        self.started.emit()

    def abort(self):
        self.reset()
        self.aborted.emit()

    def stop(self):
        self.reset()
        self.finished.emit()

    def reset(self):
        self.timer.stop()
        self.time_edit.setEnabled(True)

    def get_notify_message(self):
        return ''

    @property
    def name(self):
        return 'Alarm Clock'
Beispiel #7
0
class MineSweeper(QMainWindow):
	def __init__(self):
		super().__init__()

		self.undiscovered_color: str = "#000000"
		self.discovered_color: str = "#d9d9d9"
		self.win_timer_color: str = "#e30e0e"
		self.lose_timer_color: str = "#0cc431"


		self.curr_time = QTime(00,00,00)
		self.timer = QTimer()
		self.timer.timeout.connect(self.time)
		self.timer_already_started = False

		self.solved: bool = False
		
		self.player_ended: bool = False

		self.theme: str = "dark"

		self.list_of_mines: list = []

		self.difficulty_slider_default_value: int = 2
		self.number_of_mines: int = mines_number(NUMBER_OF_LABELS, self.difficulty_slider_default_value)


		self.create_GUI()

	def create_GUI(self) -> None:
		self.setWindowTitle("MineSweeper 1.1")

		self.win_massage = QMessageBox(self)
		self.win_massage.setText("Gratuluji, dokázal jsi nalézt všechny miny")

		self.setMouseTracking(True)

		centralWidget = QWidget(self)
		centralWidget.setStyleSheet("background: white")
		self.setCentralWidget(centralWidget)
		self.setFixedSize(X_WINDOW_SIZE + 20, Y_WINDOW_SIZE)

		layout = QGridLayout(centralWidget)
		layout.setSpacing(0)
		layout.setContentsMargins(0, 0, 0, 0)
		centralWidget.setLayout(layout)

		self.list_of_labels: list = []

		self.list_of_mines = generate_mines(self.number_of_mines, X_SIZE, Y_SIZE)

		# RESET BUTTON
		self.reset_button = QPushButton(centralWidget)
		self.reset_button.setText("RESET")
		self.reset_button.clicked.connect(self.reset)
		self.reset_button.setStyleSheet("margin: 3px")
		self.reset_button.setMinimumSize(0, 50)

		# TIMER LABEL
		self.timer_label = QLabel(centralWidget)
		self.timer_label.setText(f"{self.curr_time.minute():0>2}:{self.curr_time.second():0>2}")
		self.timer_label.setAlignment(Qt.AlignHCenter)
		self.timer_label.setStyleSheet("font: 34px")

		# DIFFICULTY SLIDER
		self.difficulty_slider = QSlider(centralWidget)
		self.difficulty_slider.setOrientation(Qt.Horizontal)
		self.difficulty_slider.setFixedHeight(30)
		self.difficulty_slider.setRange(1, 10)
		self.difficulty_slider.setTickInterval(1)
		self.difficulty_slider.setValue(self.difficulty_slider_default_value)
		self.difficulty_slider.valueChanged.connect(self.difficulty_label_set)
		self.difficulty_slider.sliderReleased.connect(self.new_mines_set)

		# DIFFICULTY LABEL
		self.difficulty_label = QLabel(centralWidget)
		self.difficulty_label.setText(str(self.difficulty_slider_default_value))
		self.difficulty_label.setAlignment(Qt.AlignCenter)
		self.difficulty_label.setStyleSheet("font: 20px")
		
		

		for i in range(Y_SIZE):
			row = []
			for j in range(X_SIZE):
				if (i, j) in self.list_of_mines:
					mine = True
				else:
					mine = False

				label = Chunk(j, i, mine)
				label.setFixedSize(FIELD_SQUARE_SIZE, FIELD_SQUARE_SIZE)
				label.setStyleSheet(f"background: {self.undiscovered_color}; border: 1px solid grey")
				layout.addWidget(label, i, j)
				row.append(label)
			self.list_of_labels.append(row)


		self.color_theme_combobox = QComboBox(centralWidget)
		self.color_theme_combobox.addItem("Dark theme", "dark")
		self.color_theme_combobox.addItem("Light theme", "light")
		self.color_theme_combobox.addItem("Color theme", "colorful")
		self.color_theme_combobox.currentIndexChanged.connect(self.theme_change)
		self.color_theme_combobox.setMinimumHeight(FIELD_SQUARE_SIZE * 2)
		if self.theme == "dark":
			self.color_theme_combobox.setCurrentIndex(0)
		elif self.theme == "light":
			self.color_theme_combobox.setCurrentIndex(1)
		else:
			self.color_theme_combobox.setCurrentIndex(2)
		layout.addWidget(self.color_theme_combobox, Y_SIZE - 2, X_SIZE, 2, 1)

		layout.addWidget(self.timer_label, 0, X_SIZE, 3, 1)
		layout.addWidget(self.reset_button, 2, X_SIZE, 3, 1)
		layout.addWidget(self.difficulty_slider, Y_SIZE, 1, 1, X_SIZE - 2)
		layout.addWidget(self.difficulty_label, Y_SIZE, X_SIZE, 1, 1)

		self.mines_number_surroundings_calculate()

	def theme_change(self) -> None:
		if self.color_theme_combobox.currentData() == "light":
			self.undiscovered_color = LIGHT_THEME["undiscovered_color"]
			self.discovered_color = LIGHT_THEME["discovered_color"]
			self.win_timer_color = LIGHT_THEME["win_timer_color"]
			self.lose_timer_color = LIGHT_THEME["lose_timer_color"]
			self.theme = "light"
	
		if self.color_theme_combobox.currentData() == "dark":	
			self.undiscovered_color = DARK_THEME["undiscovered_color"]
			self.discovered_color = DARK_THEME["discovered_color"]
			self.win_timer_color = DARK_THEME["win_timer_color"]
			self.lose_timer_color = DARK_THEME["lose_timer_color"]
			self.theme = "dark"

		if self.color_theme_combobox.currentData() == "colorful":	
			self.undiscovered_color = COLOR_THEME["undiscovered_color"]
			self.discovered_color = COLOR_THEME["discovered_color"]
			self.win_timer_color = COLOR_THEME["win_timer_color"]
			self.lose_timer_color = COLOR_THEME["lose_timer_color"]
			self.theme = "colorful"

		for y in range(Y_SIZE):
			for x in range(X_SIZE):	 
				if self.list_of_labels[y][x].marked:
					pass

				elif not self.list_of_labels[y][x].discovered:
					self.list_of_labels[y][x].setStyleSheet(f"background: {self.undiscovered_color}; border: 1px solid grey")

				elif self.list_of_labels[y][x].discovered:
					self.list_of_labels[y][x].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")
		
	def difficulty_label_set(self):
		self.difficulty_label.setText(str(self.difficulty_slider.value()))

	def mousePressEvent(self, QMouseEvent) -> None:
		if not self.player_ended:

			y = QMouseEvent.pos().x()
			x = QMouseEvent.pos().y()

			if not (x > X_GRID_SIZE or y > Y_GRID_SIZE):

				x = closest_smaller_number(x, Y_POSSIBLE_VALUES)
				y = closest_smaller_number(y, X_POSSIBLE_VALUES)

				x = int(x // FIELD_SQUARE_SIZE)
				y = int(y // FIELD_SQUARE_SIZE)

				if QMouseEvent.button() == Qt.LeftButton:
					if self.list_of_labels[x][y].mine:				
						self.stop_timer()
						if not self.player_ended:
							self.list_of_labels[x][y].discovered = True
							self.list_of_labels[x][y].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")
							self.list_of_labels[x][y].setPixmap(QPixmap("C:/Data/python/miny/pracovní verze/pictures/bomb_small.png"))
							self.win_massage.about(self, "PROHRA", "Tentokrát se to bohužel nepovedlo, snad to vyjde příště.")
					
						self.player_ended = True
					else:
						if not self.timer_already_started:
							self.start_timer()

						self.timer_already_started = True
						self.list_of_labels[x][y].discovered = True
						self.list_of_labels[x][y].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")

						self.reveal_area(y, x)

					self.solved_check()

				else:
					if not self.list_of_labels[x][y].discovered:
						if self.list_of_labels[x][y].marked:
							self.list_of_labels[x][y].setStyleSheet(f"background: {self.undiscovered_color}; border: 1px solid grey")
							self.list_of_labels[x][y].marked = False

						else:
							self.list_of_labels[x][y].setStyleSheet("background: orange; border: 1px solid grey")
							self.list_of_labels[x][y].marked = True

	def mines_number_surroundings_calculate(self) -> None:
		for x in range(X_SIZE):
			for y in range(Y_SIZE):
				self.list_of_labels[x][y].mines_number_surroundings = 0
				for i in range(x - 1, x + 2):
					for j in range(y - 1, y + 2):
						try:
							if self.list_of_labels[i][j].mine and i >= 0 and j >= 0:
								if not (i == x and j == y):
									self.list_of_labels[x][y].mines_number_surroundings += 1
						except IndexError:
							pass

	def new_mines_set(self):
		self.number_of_mines = mines_number(NUMBER_OF_LABELS, self.difficulty_slider.value())
		self.list_of_mines = generate_mines(self.number_of_mines, X_SIZE, Y_SIZE)
		for y in range(Y_SIZE):
			for x in range(X_SIZE):
				if (y, x) in self.list_of_mines:
					self.list_of_labels[y][x].mine = True
				else:
					self.list_of_labels[y][x].mine = False

		self.mines_number_surroundings_calculate()
		self.label_set()

	def label_set(self) -> None:
		for y in range(Y_SIZE):
			for x in range(X_SIZE):
				if self.list_of_labels[y][x].discovered:
					if self.list_of_labels[y][x].mines_number_surroundings == 0:
						pass
					else:
						self.list_of_labels[y][x].setText(str(self.list_of_labels[y][x].mines_number_surroundings))

	def reveal_area(self, x: int, y: int) -> None:
		if self.list_of_labels[y][x].mines_number_surroundings == 0:		
			try:
				extract = self.list_of_labels[y - 1][x]

				if not extract.mine and Y_SIZE > y - 1 >= 0 and not extract.discovered:
					self.list_of_labels[y - 1][x].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")
					self.list_of_labels[y - 1][x].discovered = True
					if extract.mines_number_surroundings == 0:
						self.reveal_area(x, y - 1)

			except IndexError:
				pass

			try:
				extract = self.list_of_labels[y + 1][x]

				if not extract.mine and Y_SIZE > y + 1 >= 0 and not extract.discovered:
					self.list_of_labels[y + 1][x].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")
					self.list_of_labels[y + 1][x].discovered = True
					if extract.mines_number_surroundings == 0:
						self.reveal_area(x, y + 1)

			except IndexError:
				pass

			try:
				extract = self.list_of_labels[y][x + 1]

				if not extract.mine and X_SIZE > x + 1 >= 0 and not extract.discovered:
					self.list_of_labels[y][x + 1].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")
					self.list_of_labels[y][x + 1].discovered = True
					if extract.mines_number_surroundings == 0:
						self.reveal_area(x + 1, y)

			except IndexError:
				pass

			try:
				extract = self.list_of_labels[y][x - 1]

				if not extract.mine and X_SIZE - 1 > x  - 1 >= 0 and not extract.discovered:
					self.list_of_labels[y][x - 1].setStyleSheet(f"background: {self.discovered_color}; border: 1px solid grey")
					self.list_of_labels[y][x - 1].discovered = True
					if extract.mines_number_surroundings == 0:
						self.reveal_area(x - 1, y)

			except IndexError:
				pass

		self.label_set()

	def solved_check(self) -> None:
		for element in self.list_of_labels:
			for part in element:
				if not part.mine and not part.discovered:
					return

		self.solved = True
		self.stop_timer()

		if not self.player_ended:
			self.player_ended = True
			self.win_massage.about(self, "VÝHRA", f"Gratuluji, zvládl/a jsi vyřešit tento problém. Zvládl/a jsi to za {self.curr_time.minute():0>2}:{self.curr_time.second():0>2}")

	# TIMER FUNCIONS
	def start_timer(self) -> None:
		self.difficulty_slider.setDisabled(True)
		self.timer.start(1000)
		
	def stop_timer(self) -> None:
		self.timer.stop()
		if not self.solved:
			self.timer_label.setStyleSheet(f"font: 34px; color: {self.win_timer_color}")
		else:
			self.timer_label.setStyleSheet(f"font: 34px; color: {self.lose_timer_color}")

	def time(self) -> None:
		self.curr_time = self.curr_time.addSecs(1)
		self.timer_label.setText(f"{self.curr_time.minute():0>2}:{self.curr_time.second():0>2}")

	#RESET
	def reset(self) -> None:
		self.timer = QTimer()
		self.curr_time = QTime(00,00,00)
		self.timer.timeout.connect(self.time)
		self.solved = False
		self.timer_already_started = False
		self.player_ended = False
		self.difficulty_slider.setDisabled(False)
		self.difficulty_slider_default_value = self.difficulty_slider.value()
		self.create_GUI()
Beispiel #8
0
class QVTKRenderWindowInteractor(QVTKRWIBaseClass):
    """ 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, **kw):
        # the current button
        self._ActiveButton = Qt.NoButton

        # private attributes
        self.__saveX = 0
        self.__saveY = 0
        self.__saveModifiers = Qt.NoModifier
        self.__saveButtons = Qt.NoButton
        self.__wheelDelta = 0

        # 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 base qt-level widget
        if QVTKRWIBase == "QWidget":
            if "wflags" in kw:
                wflags = kw['wflags']
            else:
                wflags = Qt.WindowFlags()
            QWidget.__init__(self, parent, wflags | Qt.MSWindowsOwnDC)
        elif QVTKRWIBase == "QOpenGLWidget":
            QOpenGLWidget.__init__(self, parent)

        if rw:  # user-supplied render window
            self._RenderWindow = rw
        else:
            self._RenderWindow = vtkRenderWindow()

        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)

        self._RenderWindow.SetWindowInfo(str(int(WId)))

        if stereo:  # stereo mode
            self._RenderWindow.StereoCapableWindowOn()
            self._RenderWindow.SetStereoTypeToCrystalEyes()

        try:
            self._Iren = kw['iren']
        except KeyError:
            self._Iren = 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)

        self._Iren.AddObserver('CreateTimerEvent', self.CreateTimer)
        self._Iren.AddObserver('DestroyTimerEvent', self.DestroyTimer)
        self._Iren.GetRenderWindow().AddObserver('CursorChangedEvent',
                                                 self.CursorChangedEvent)

        # If we've a parent, it does not close the child when closed.
        # Connect the parent's destroyed signal to this widget's close
        # slot for proper cleanup of VTK objects.
        if self.parent():
            self.parent().destroyed.connect(self.close, Qt.DirectConnection)

    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 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._Iren.Render()

    def resizeEvent(self, ev):
        scale = self._getPixelRatio()
        w = int(round(scale * self.width()))
        h = int(round(scale * self.height()))
        self._RenderWindow.SetDPI(int(round(72 * scale)))
        vtkRenderWindow.SetSize(self._RenderWindow, w, h)
        self._Iren.SetSize(w, h)
        self._Iren.ConfigureEvent()
        self.update()

    def _GetKeyCharAndKeySym(self, ev):
        """ Convert a Qt key into a char and a vtk keysym.
        This is essentially copied from the c++ implementation in
        GUISupport/Qt/QVTKInteractorAdapter.cxx.
        """
        # if there is a char, convert its ASCII code to a VTK keysym
        try:
            keyChar = ev.text()[0]
            keySym = _keysyms_for_ascii[ord(keyChar)]
        except IndexError:
            keyChar = '\0'
            keySym = None

        # next, try converting Qt key code to a VTK keysym
        if keySym is None:
            try:
                keySym = _keysyms[ev.key()]
            except KeyError:
                keySym = None

        # use "None" as a fallback
        if keySym is None:
            keySym = "None"

        return keyChar, keySym

    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

    @staticmethod
    def _getPixelRatio():
        if PyQtImpl in ["PyQt5", "PySide2", "PySide6"]:
            # Source: https://stackoverflow.com/a/40053864/3388962
            pos = QCursor.pos()
            for screen in QApplication.screens():
                rect = screen.geometry()
                if rect.contains(pos):
                    return screen.devicePixelRatio()
            # Should never happen, but try to find a good fallback.
            return QApplication.instance().devicePixelRatio()
        else:
            # Qt4 seems not to provide any cross-platform means to get the
            # pixel ratio.
            return 1.

    def _setEventInformation(self,
                             x,
                             y,
                             ctrl,
                             shift,
                             key,
                             repeat=0,
                             keysum=None):
        scale = self._getPixelRatio()
        self._Iren.SetEventInformation(
            int(round(x * scale)), int(round((self.height() - y - 1) * scale)),
            ctrl, shift, key, repeat, keysum)

    def enterEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(self.__saveX, self.__saveY, ctrl, shift,
                                  chr(0), 0, None)
        self._Iren.EnterEvent()

    def leaveEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(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._setEventInformation(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.MiddleButton:
            self._Iren.MiddleButtonPressEvent()

    def mouseReleaseEvent(self, ev):
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(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.MiddleButton:
            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._setEventInformation(ev.x(), ev.y(), ctrl, shift, chr(0), 0, None)
        self._Iren.MouseMoveEvent()

    def keyPressEvent(self, ev):
        key, keySym = self._GetKeyCharAndKeySym(ev)
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(self.__saveX, self.__saveY, ctrl, shift, key,
                                  0, keySym)
        self._Iren.KeyPressEvent()
        self._Iren.CharEvent()

    def keyReleaseEvent(self, ev):
        key, keySym = self._GetKeyCharAndKeySym(ev)
        ctrl, shift = self._GetCtrlShift(ev)
        self._setEventInformation(self.__saveX, self.__saveY, ctrl, shift, key,
                                  0, keySym)
        self._Iren.KeyReleaseEvent()

    def wheelEvent(self, ev):
        if hasattr(ev, 'delta'):
            self.__wheelDelta += ev.delta()
        else:
            self.__wheelDelta += ev.angleDelta().y()

        if self.__wheelDelta >= 120:
            self._Iren.MouseWheelForwardEvent()
            self.__wheelDelta = 0
        elif self.__wheelDelta <= -120:
            self._Iren.MouseWheelBackwardEvent()
            self.__wheelDelta = 0

    def GetRenderWindow(self):
        return self._RenderWindow

    def Render(self):
        self.update()
class GameConnection(QObject, ConnectionBackend):
    Updated = Signal()

    _dt: float = 2.5
    _last_status: Any = None
    _permanent_pickups: List[Tuple[str, PickupEntry]]

    def __init__(self, executor: MemoryOperationExecutor):
        super().__init__()
        ConnectionBackend.__init__(self, executor)
        self._permanent_pickups = []

        self._timer = QTimer(self)
        self._timer.timeout.connect(self._auto_update)
        self._timer.setInterval(self._dt * 1000)
        self._timer.setSingleShot(True)
        self._notify_status()

    def set_executor(self, executor: MemoryOperationExecutor):
        self.executor = executor
        self._notify_status()

    async def start(self):
        self._timer.start()

    async def stop(self):
        self._timer.stop()

    @asyncSlot()
    async def _auto_update(self):
        try:
            await self.update(self._dt)
            self._notify_status()
        finally:
            self._timer.start()

    def _notify_status(self):
        new_status = self.current_status
        inventory = self.get_current_inventory()

        if self._last_status != (new_status, self.executor, inventory):
            self._last_status = (new_status, self.executor,
                                 copy.copy(inventory))
            self.Updated.emit()

    @property
    def pretty_current_status(self) -> str:
        return f"{self.backend_choice.pretty_text}: {self.current_status.pretty_text}"

    @property
    def current_game_name(self) -> Optional[str]:
        if self.connector is not None:
            return self.connector.game_enum.long_name

    @property
    def name(self) -> str:
        raise ValueError("bleh")

    def set_location_collected_listener(self,
                                        listener: Optional[LocationListener]):
        super(ConnectionBackend,
              self).set_location_collected_listener(listener)
        self.checking_for_collected_index = listener is not None
Beispiel #10
0
class DebugView(QWidget, View):
	class DebugViewHistoryEntry(HistoryEntry):
		def __init__(self, memory_addr, address, is_raw):
			HistoryEntry.__init__(self)

			self.memory_addr = memory_addr
			self.address = address
			self.is_raw = is_raw

		def __repr__(self):
			if self.is_raw:
				return "<raw history: {}+{:0x} (memory: {:0x})>".format(self.address['module'], self.address['offset'], self.memory_addr)
			return "<code history: {:0x} (memory: {:0x})>".format(self.address, self.memory_addr)

	def __init__(self, parent, data):
		if not type(data) == BinaryView:
			raise Exception('expected widget data to be a BinaryView')

		self.bv = data

		self.debug_state = binjaplug.get_state(data)
		memory_view = self.debug_state.memory_view
		self.debug_state.ui.debug_view = self

		QWidget.__init__(self, parent)
		self.controls = ControlsWidget.DebugControlsWidget(self, "Controls", data, self.debug_state)
		View.__init__(self)

		self.setupView(self)

		self.current_offset = 0

		self.splitter = QSplitter(Qt.Orientation.Horizontal, self)

		frame = ViewFrame.viewFrameForWidget(self)
		self.memory_editor = LinearView(memory_view, frame)
		self.binary_editor = DisassemblyContainer(frame, data, frame)

		self.binary_text = TokenizedTextView(self, memory_view)
		self.is_raw_disassembly = False
		self.raw_address = 0

		self.is_navigating_history = False
		self.memory_history_addr = 0

		# TODO: Handle these and change views accordingly
		# Currently they are just disabled as the DisassemblyContainer gets confused
		# about where to go and just shows a bad view
		self.binary_editor.getDisassembly().actionHandler().bindAction("View in Hex Editor", UIAction())
		self.binary_editor.getDisassembly().actionHandler().bindAction("View in Linear Disassembly", UIAction())
		self.binary_editor.getDisassembly().actionHandler().bindAction("View in Types View", UIAction())

		self.memory_editor.actionHandler().bindAction("View in Hex Editor", UIAction())
		self.memory_editor.actionHandler().bindAction("View in Disassembly Graph", UIAction())
		self.memory_editor.actionHandler().bindAction("View in Types View", UIAction())

		small_font = QApplication.font()
		small_font.setPointSize(11)

		bv_layout = QVBoxLayout()
		bv_layout.setSpacing(0)
		bv_layout.setContentsMargins(0, 0, 0, 0)

		bv_label = QLabel("Loaded File")
		bv_label.setFont(small_font)
		bv_layout.addWidget(bv_label)
		bv_layout.addWidget(self.binary_editor)

		self.bv_widget = QWidget()
		self.bv_widget.setLayout(bv_layout)

		disasm_layout = QVBoxLayout()
		disasm_layout.setSpacing(0)
		disasm_layout.setContentsMargins(0, 0, 0, 0)

		disasm_label = QLabel("Raw Disassembly at PC")
		disasm_label.setFont(small_font)
		disasm_layout.addWidget(disasm_label)
		disasm_layout.addWidget(self.binary_text)

		self.disasm_widget = QWidget()
		self.disasm_widget.setLayout(disasm_layout)

		memory_layout = QVBoxLayout()
		memory_layout.setSpacing(0)
		memory_layout.setContentsMargins(0, 0, 0, 0)

		memory_label = QLabel("Debugged Process")
		memory_label.setFont(small_font)
		memory_layout.addWidget(memory_label)
		memory_layout.addWidget(self.memory_editor)

		self.memory_widget = QWidget()
		self.memory_widget.setLayout(memory_layout)

		self.splitter.addWidget(self.bv_widget)
		self.splitter.addWidget(self.memory_widget)

		# Equally sized
		self.splitter.setSizes([0x7fffffff, 0x7fffffff])

		layout = QVBoxLayout()
		layout.setContentsMargins(0, 0, 0, 0)
		layout.setSpacing(0)
		layout.addWidget(self.controls)
		layout.addWidget(self.splitter, 100)
		self.setLayout(layout)

		self.needs_update = True
		self.update_timer = QTimer(self)
		self.update_timer.setInterval(200)
		self.update_timer.setSingleShot(False)
		self.update_timer.timeout.connect(lambda: self.updateTimerEvent())

		self.add_scripting_ref()

		# set initial breakpoint when view is switched
		if self.debug_state.bv and self.debug_state.bv.entry_point:
			local_entry_offset = self.debug_state.bv.entry_point - self.debug_state.bv.start
			if not self.debug_state.breakpoints.contains_offset(self.debug_state.bv.file.original_filename, local_entry_offset):
				self.debug_state.breakpoints.add_offset(self.debug_state.bv.file.original_filename, local_entry_offset)
				if self.debug_state.ui is not None:
					self.debug_state.ui.breakpoint_tag_add(self.debug_state.bv.entry_point)
					self.debug_state.ui.update_highlights()
					self.debug_state.ui.update_breakpoints()

	def add_scripting_ref(self):
		# Hack: The interpreter is just a thread, so look through all threads
		# and assign our state to the interpreter's locals
		for thread in threading.enumerate():
			if type(thread) == PythonScriptingInstance.InterpreterThread:
				thread.locals["dbg"] = self.debug_state

	def getData(self):
		return self.bv

	def getFont(self):
		return binaryninjaui.getMonospaceFont(self)

	def getCurrentOffset(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getCurrentOffset()
		return self.raw_address

	def getSelectionOffsets(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getSelectionOffsets()
		return (self.raw_address, self.raw_address)

	def getCurrentFunction(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getCurrentFunction()
		return None

	def getCurrentBasicBlock(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getCurrentBasicBlock()
		return None

	def getCurrentArchitecture(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getCurrentArchitecture()
		return None

	def getCurrentLowLevelILFunction(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getCurrentLowLevelILFunction()
		return None

	def getCurrentMediumLevelILFunction(self):
		if not self.is_raw_disassembly:
			return self.binary_editor.getDisassembly().getCurrentMediumLevelILFunction()
		return None

	def getHistoryEntry(self):
		if self.is_navigating_history:
			return None
		memory_addr = self.memory_editor.getCurrentOffset()
		if memory_addr != self.memory_history_addr:
			self.memory_history_addr = memory_addr
		if self.is_raw_disassembly and self.debug_state.connected:
			rel_addr = self.debug_state.modules.absolute_addr_to_relative(self.raw_address)
			return DebugView.DebugViewHistoryEntry(memory_addr, rel_addr, True)
		else:
			address = self.binary_editor.getDisassembly().getCurrentOffset()
			return DebugView.DebugViewHistoryEntry(memory_addr, address, False)

	def navigateToFunction(self, func, offset):
		return self.navigate(offset)

	def navigateToHistoryEntry(self, entry):
		self.is_navigating_history = True
		if hasattr(entry, 'is_raw'):
			self.memory_editor.navigate(entry.memory_addr)
			if entry.is_raw:
				if self.debug_state.connected:
					address = self.debug_state.modules.relative_addr_to_absolute(entry.address)
					self.navigate_raw(address)
			else:
				self.navigate_live(entry.address)

		View.navigateToHistoryEntry(self, entry)
		self.is_navigating_history = False

	def navigate(self, addr):
		# If we're not connected we cannot even check if the address is remote
		if not self.debug_state.connected:
			return self.navigate_live(addr)

		if self.debug_state.memory_view.is_local_addr(addr):
			local_addr = self.debug_state.memory_view.remote_addr_to_local(addr)
			if self.debug_state.bv.read(local_addr, 1) and len(self.debug_state.bv.get_functions_containing(local_addr)) > 0:
				return self.navigate_live(local_addr)

		# This runs into conflicts if some other address space is mapped over
		# where the local BV is currently loaded, but this is was less likely
		# than the user navigating to a function from the UI
		if self.debug_state.bv.read(addr, 1) and len(self.debug_state.bv.get_functions_containing(addr)) > 0:
			return self.navigate_live(addr)

		return self.navigate_raw(addr)

	def navigate_live(self, addr):
		self.show_raw_disassembly(False)
		return self.binary_editor.getDisassembly().navigate(addr)

	def navigate_raw(self, addr):
		if not self.debug_state.connected:
			# Can't navigate to remote addr when disconnected
			return False
		self.raw_address = addr
		self.show_raw_disassembly(True)
		self.load_raw_disassembly(addr)
		return True

	def notifyMemoryChanged(self):
		self.needs_update = True

	def updateTimerEvent(self):
		if self.needs_update:
			self.needs_update = False

			# Refresh the editor
			if not self.debug_state.connected:
				self.memory_editor.navigate(0)
				return

			# self.memory_editor.navigate(self.debug_state.stack_pointer)

	def showEvent(self, event):
		if not event.spontaneous():
			self.update_timer.start()
			self.add_scripting_ref()

	def hideEvent(self, event):
		if not event.spontaneous():
			self.update_timer.stop()

	def shouldBeVisible(self, view_frame):
		if view_frame is None:
			return False
		else:
			return True

	def load_raw_disassembly(self, start_ip):
		# Read a few instructions from rip and disassemble them
		inst_count = 50

		arch_dis = self.debug_state.remote_arch
		rip = self.debug_state.ip

		# Assume the worst, just in case
		read_length = arch_dis.max_instr_length * inst_count
		data = self.debug_state.memory_view.read(start_ip, read_length)

		lines = []

		# Append header line
		tokens = [InstructionTextToken(InstructionTextTokenType.TextToken, "(Code not backed by loaded file, showing only raw disassembly)")]
		contents = DisassemblyTextLine(tokens, start_ip)
		line = LinearDisassemblyLine(LinearDisassemblyLineType.BasicLineType, None, None, contents)
		lines.append(line)

		total_read = 0
		for i in range(inst_count):
			line_addr = start_ip + total_read
			(insn_tokens, length) = arch_dis.get_instruction_text(data[total_read:], line_addr)

			if insn_tokens is None:
				insn_tokens = [InstructionTextToken(InstructionTextTokenType.TextToken, "??")]
				length = arch_dis.instr_alignment
				if length == 0:
					length = 1

			# terrible libshiboken workaround, see #101
			for tok in insn_tokens:
				if tok.value.bit_length() == 64:
					tok.value ^= 0x8000000000000000

			tokens = []
			color = HighlightStandardColor.NoHighlightColor
			if line_addr == rip:
				if self.debug_state.breakpoints.contains_absolute(start_ip + total_read):
					# Breakpoint & pc
					tokens.append(InstructionTextToken(InstructionTextTokenType.TagToken, self.debug_state.ui.get_breakpoint_tag_type().icon + ">", width=5))
					color = HighlightStandardColor.RedHighlightColor
				else:
					# PC
					tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ==> "))
					color = HighlightStandardColor.BlueHighlightColor
			else:
				if self.debug_state.breakpoints.contains_absolute(start_ip + total_read):
					# Breakpoint
					tokens.append(InstructionTextToken(InstructionTextTokenType.TagToken, self.debug_state.ui.get_breakpoint_tag_type().icon, width=5))
					color = HighlightStandardColor.RedHighlightColor
				else:
					# Regular line
					tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "     "))
			# Address
			tokens.append(InstructionTextToken(InstructionTextTokenType.AddressDisplayToken, hex(line_addr)[2:], line_addr))
			tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "  "))
			tokens.extend(insn_tokens)

			# Convert to linear disassembly line
			contents = DisassemblyTextLine(tokens, line_addr, color=color)
			line = LinearDisassemblyLine(LinearDisassemblyLineType.CodeDisassemblyLineType, None, None, contents)
			lines.append(line)

			total_read += length

		self.binary_text.setLines(lines)

	def show_raw_disassembly(self, raw):
		if raw != self.is_raw_disassembly:
			self.splitter.replaceWidget(0, self.disasm_widget if raw else self.bv_widget)
			self.is_raw_disassembly = raw

	def refresh_raw_disassembly(self):
		if not self.debug_state.connected:
			# Can't navigate to remote addr when disconnected
			return

		if self.is_raw_disassembly:
			self.load_raw_disassembly(self.getCurrentOffset())
Beispiel #11
0
class SettingsTree(QTreeWidget):
    def __init__(self, parent=None):
        super(SettingsTree, self).__init__(parent)

        self._type_checker = TypeChecker()
        self.setItemDelegate(VariantDelegate(self._type_checker, self))

        self.setHeaderLabels(("Setting", "Type", "Value"))
        self.header().setSectionResizeMode(0, QHeaderView.Stretch)
        self.header().setSectionResizeMode(2, QHeaderView.Stretch)

        self.settings = None
        self.refresh_timer = QTimer()
        self.refresh_timer.setInterval(2000)
        self.auto_refresh = False

        self.group_icon = QIcon()
        style = self.style()
        self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirClosedIcon),
                                  QIcon.Normal, QIcon.Off)
        self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirOpenIcon),
                                  QIcon.Normal, QIcon.On)
        self.key_icon = QIcon()
        self.key_icon.addPixmap(style.standardPixmap(QStyle.SP_FileIcon))

        self.refresh_timer.timeout.connect(self.maybe_refresh)

    def set_settings_object(self, settings):
        self.settings = settings
        self.clear()

        if self.settings is not None:
            self.settings.setParent(self)
            self.refresh()
            if self.auto_refresh:
                self.refresh_timer.start()
        else:
            self.refresh_timer.stop()

    def sizeHint(self):
        return QSize(800, 600)

    def set_auto_refresh(self, autoRefresh):
        self.auto_refresh = autoRefresh

        if self.settings is not None:
            if self.auto_refresh:
                self.maybe_refresh()
                self.refresh_timer.start()
            else:
                self.refresh_timer.stop()

    def set_fallbacks_enabled(self, enabled):
        if self.settings is not None:
            self.settings.setFallbacksEnabled(enabled)
            self.refresh()

    def maybe_refresh(self):
        if self.state() != QAbstractItemView.EditingState:
            self.refresh()

    def refresh(self):
        if self.settings is None:
            return

        # The signal might not be connected.
        try:
            self.itemChanged.disconnect(self.update_setting)
        except:
            pass

        self.settings.sync()
        self.update_child_items(None)

        self.itemChanged.connect(self.update_setting)

    def event(self, event):
        if event.type() == QEvent.WindowActivate:
            if self.isActiveWindow() and self.auto_refresh:
                self.maybe_refresh()

        return super(SettingsTree, self).event(event)

    def update_setting(self, item):
        key = item.text(0)
        ancestor = item.parent()

        while ancestor:
            key = ancestor.text(0) + '/' + key
            ancestor = ancestor.parent()

        d = item.data(2, Qt.UserRole)
        self.settings.setValue(key, item.data(2, Qt.UserRole))

        if self.auto_refresh:
            self.refresh()

    def update_child_items(self, parent):
        divider_index = 0

        for group in self.settings.childGroups():
            child_index = self.find_child(parent, group, divider_index)
            if child_index != -1:
                child = self.child_at(parent, child_index)
                child.setText(1, '')
                child.setText(2, '')
                child.setData(2, Qt.UserRole, None)
                self.move_item_forward(parent, child_index, divider_index)
            else:
                child = self.create_item(group, parent, divider_index)

            child.setIcon(0, self.group_icon)
            divider_index += 1

            self.settings.beginGroup(group)
            self.update_child_items(child)
            self.settings.endGroup()

        for key in self.settings.childKeys():
            child_index = self.find_child(parent, key, 0)
            if child_index == -1 or child_index >= divider_index:
                if child_index != -1:
                    child = self.child_at(parent, child_index)
                    for i in range(child.childCount()):
                        self.delete_item(child, i)
                    self.move_item_forward(parent, child_index, divider_index)
                else:
                    child = self.create_item(key, parent, divider_index)
                child.setIcon(0, self.key_icon)
                divider_index += 1
            else:
                child = self.child_at(parent, child_index)

            value = self.settings.value(key)
            if value is None:
                child.setText(1, 'Invalid')
            else:
                # Try to convert to type unless a QByteArray is received
                if isinstance(value, str):
                    value_type = self._type_checker.type_from_text(value)
                    if value_type:
                        value = self.settings.value(key, type=value_type)
                child.setText(1, value.__class__.__name__)
            child.setText(2, VariantDelegate.displayText(value))
            child.setData(2, Qt.UserRole, value)

        while divider_index < self.child_count(parent):
            self.delete_item(parent, divider_index)

    def create_item(self, text, parent, index):
        after = None

        if index != 0:
            after = self.child_at(parent, index - 1)

        if parent is not None:
            item = QTreeWidgetItem(parent, after)
        else:
            item = QTreeWidgetItem(self, after)

        item.setText(0, text)
        item.setFlags(item.flags() | Qt.ItemIsEditable)
        return item

    def delete_item(self, parent, index):
        if parent is not None:
            item = parent.takeChild(index)
        else:
            item = self.takeTopLevelItem(index)
        del item

    def child_at(self, parent, index):
        if parent is not None:
            return parent.child(index)
        else:
            return self.topLevelItem(index)

    def child_count(self, parent):
        if parent is not None:
            return parent.childCount()
        else:
            return self.topLevelItemCount()

    def find_child(self, parent, text, startIndex):
        for i in range(self.child_count(parent)):
            if self.child_at(parent, i).text(0) == text:
                return i
        return -1

    def move_item_forward(self, parent, oldIndex, newIndex):
        for int in range(oldIndex - newIndex):
            self.delete_item(parent, newIndex)
Beispiel #12
0
class Emotion:
    def __init__(self, main_window):
        self.main_window = main_window
        self.timer = QTimer()
        self.timer.timeout.connect(self.view_cam)
        self.cap = cv2.VideoCapture(0)
        self.data_path = os.path.join(os.path.dirname(__file__), 'Data')
        self.detector = dlib.get_frontal_face_detector()
        self.predictor = dlib.shape_predictor(
            os.path.join(self.data_path,
                         'shape_predictor_68_face_landmarks.dat'))
        self.face_rec = dlib.face_recognition_model_v1(
            os.path.join(self.data_path,
                         'dlib_face_recognition_resnet_model_v1.dat'))
        self.emotions = [
            'Радость', 'Удивление', 'Грусть', 'Злость', 'Отвращение',
            'Презрение', 'Страх'
        ]
        self.fdi = []
        self.ind_d = 0
        self.ind_u = 0
        self.ind_p = 0
        self.image = None
        self.P_em = 0.5
        self.f = open(os.path.join(self.data_path, 'svm_dat.dat'), 'rb')
        self.clf = pickle.load(self.f)
        self.emotion_result = 0
        self.nn = 20
        self.e = None
        self.label = None
        self.font = ImageFont.truetype(
            os.path.join(os.path.dirname(__file__), 'pictures',
                         'DejaVuSans.ttf'), 18)
        self.shape = None
        self.i = 0
        self.step = 0
        self.width = 0
        self.height = 0
        self.em_n = 0

    def view_cam(self):
        ret, self.image = self.cap.read()
        self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
        self.height, self.width, channel = self.image.shape
        self.step = channel * self.width
        self.rectangle_face()
        q_img = QImage(self.image.data, self.width, self.height, self.step,
                       QImage.Format_RGB888)
        self.main_window.ui.label_3.setPixmap(QPixmap.fromImage(q_img))

    def start(self, e):
        self.e = e
        self.main_window.ui = Ui_Simulator()
        self.main_window.ui.setupUi(self.main_window)
        callback = functools.partial(self.emotion, e=e, cmd='stop')
        self.main_window.ui.pushButton.clicked.connect(callback)
        self.main_window.ui.label.setPixmap(
            QPixmap(
                os.path.join(os.path.dirname(__file__), 'pictures',
                             f'{str(e)}.jpg')))
        self.main_window.setWindowTitle('Тренажер')
        self.timer.start(30)

    def emotion(self, e: int, cmd='sim'):
        if cmd == 'sim':
            self.start(e)
        elif cmd == 'stop':
            self.timer.stop()
            self.main_window.main_window.select_emotions_window()

    def rectangle_face(self):
        detection = self.detector(self.image, 1)
        if len(detection) == 0:
            self.main_window.ui.label_2.setText('Нет никого в кадре')
            self.em_n = 0
        for k, d in enumerate(detection):
            if self.i % 3 == 0:
                self.shape = self.predictor(self.image, d)
                face_description = self.face_rec.compute_face_descriptor(
                    self.image, self.shape)
                dat = [face_description]
                self.em_n = self.clf.predict(dat)
            utils.rectangle_face(self.image, detection, self.shape)
            img_pil = Image.fromarray(self.image)
            draw = ImageDraw.Draw(img_pil)
            if self.em_n == self.e:
                self.emotion_result += 1
                draw.text((detection[0].left(), detection[0].top() - 20),
                          self.emotions[int(self.em_n) - 1],
                          font=self.font,
                          fill=(0, 255, 0))
            else:
                draw.text((detection[0].left(), detection[0].top() - 20),
                          self.emotions[int(self.em_n) - 1],
                          font=self.font,
                          fill=(255, 0, 0))
            self.image = np.array(img_pil)
        cv2.waitKey(10)
        if self.i == self.nn:
            self.emotion_result = self.emotion_result / self.nn
            if self.emotion_result >= self.P_em:
                self.main_window.ui.label_2.setText(
                    "Молодец:  " +
                    "{0:.2f}".format(self.emotion_result * 100) + '%')
                self.emotion(self.e, 'stop')
            else:
                self.main_window.ui.label_2.setText(
                    "Попробуй ещё:  " +
                    "{0:.2f}".format(self.emotion_result * 100) + '%')
            self.i = 0
        else:
            self.i += 1
Beispiel #13
0
class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_TrainControllerSW()
        self.ui.setupUi(self)

        #automatic mode stuff
        self.autoMode = True
        self.ui.automaticMode.toggled.connect(self.onAutoManual)
        self.onAutoManual()

        #commanded speed stuff
        self.setPointSpeed = 0
        self.ui.setPointSpeedVal.display(self.setPointSpeed)
        self.ui.speedUpButton.clicked.connect(self.increaseSetPoint)
        self.ui.speedDownButton.clicked.connect(self.decreaseSetPoint)

        #service brake stuff
        self.serviceBrake = False
        self.ui.serviceBrake.pressed.connect(self.serviceBrakeActivated)
        self.ui.serviceBrake.released.connect(self.serviceBrakeDeactivated)

        #service Brake Failure Stuff
        self.ui.causeBrakeFailure.clicked.connect(self.causeBrakeFailure)

        #emergency brake stuff
        self.ui.emergencyBrake.pressed.connect(self.onEmergencyBrake)
        self.ui.emergencyBrake.released.connect(self.onEmergencyBrakeOff)
        self.emergencyBrake = False
        #encoded Track Circuit stuff
        self.actualSpeed = float(0)
        self.actualSpeedLast = float(0)
        self.actualSpeedSecond = float(0)
        self.commandedSpeed = float(0)
        self.authority = float(0)
        self.encodedTC = 0
        self.ui.updateTCFake.clicked.connect(self.onUpdateTCFake)
        self.power = float(0)

        #passenger brake fake stuff
        self.ui.causePassenger.clicked.connect(self.onPassengerBrakeActivated)

        #actual speed fake stuff
        self.ui.updateActualFake.clicked.connect(self.onUpdateActualFake)

        #beacon stuff
        self.ui.updateBeaconFake.clicked.connect(self.onUpdateBeaconFake)
        self.encodedBeacon = 0
        self.upcomingStation = False
        self.leftDoorsOpen = False
        self.rightDoorsOpen = False
        self.exteriorLightsOn = False

        self.station = " "
        #setting up stations
        self.stationArray = ("Shadyside", "Herron Ave", "Swissville",
                             "Penn Station", "Steel Plaza", "First Ave",
                             "Station Square", "South Hills Junction",
                             "Pioneer", "Edgebrook", "Whited", "South Bank",
                             "Central", "Inglewood", "Overbrook", "Glenburry",
                             "Dormont", "Mt Lebanon", "Poplar",
                             "Castle Shannon")

        #pid loop updates
        self.ui.updatePID.clicked.connect(self.onUpdateKpKi)

        #pid loop stuff
        self.kp = 0
        self.ki = 0
        self.pid = PID(self.kp, self.ki, 0)
        self.pid.output_limits = (0, 120000)
        self.pidTimer = QTimer()
        self.pidTimer.timeout.connect(self.pidLoop)
        self.pidTimer.start(1000)

        #cause engine failure
        self.ui.causeEngineFailure.clicked.connect(self.causeEngineFailure)

        #Announcements
        self.announcement = ""
        self.ui.intercom.clicked.connect(self.onAnnouncement)

        self.ui.temperature.valueChanged.connect(self.onTemperatureChange)
        self.temperature = 70

        #auto mode loop
        self.alreadyWaiting = False

        #manual mode loop
        self.openedl = False
        self.openedr = False

        #lights sending to test ui

        self.ui.interiorLights_2.setEnabled(False)
        self.ui.exteriorLights_2.setEnabled(False)
        self.ui.interiorLights.stateChanged.connect(self.onIntLightsChanged)
        self.ui.exteriorLights.stateChanged.connect(self.onExtLightsChanged)

    #increase: used to increase setpoint speed
    def increaseSetPoint(self):
        if (self.setPointSpeed + 1 <= self.commandedSpeed):
            self.setPointSpeed = self.setPointSpeed + 1
            print(self.setPointSpeed)
        self.displayUpdate()

    #decreaseSetPoint: used to decrease setpoint speed
    def decreaseSetPoint(self):
        if (self.setPointSpeed > 0):
            self.setPointSpeed = self.setPointSpeed - 1
        print(self.setPointSpeed)
        self.displayUpdate()

    #displayUpdate: used to update all display features
    def displayUpdate(self):
        self.ui.setPointSpeedVal.display(self.setPointSpeed)
        self.ui.commandedSpeedVal.display(self.commandedSpeed)
        self.ui.authority.display(self.authority)
        self.ui.actualSpeedVal.display(self.actualSpeed)
        self.ui.encodedTC.setPlainText(str(bin(self.encodedTC)))

        if (self.serviceBrake):
            self.ui.textBrowser_9.setPlainText("S Brake Active")
            self.ui.textBrowser_11.setPlainText("S Brake Active")
        else:
            self.ui.textBrowser_9.setPlainText("S Brake Inactive")
            self.ui.textBrowser_11.setPlainText("S Brake Inactive")

        if (self.emergencyBrake):
            self.ui.textBrowser_8.setPlainText("E Brake Active")
            self.ui.textBrowser_10.setPlainText("E Brake Active")
        else:
            self.ui.textBrowser_8.setPlainText("E Brake Inactive")
            self.ui.textBrowser_10.setPlainText("E Brake Inactive")

        #to be removed once fake inputs are stripped
        if (self.autoMode == True):
            self.ui.leftDoors.setChecked(self.leftDoorsOpen)
            self.ui.rightDoors.setChecked(self.rightDoorsOpen)

        #when you are in not auto mode
        if (self.autoMode == False):
            self.leftDoors = self.ui.leftDoors.isChecked()
            self.rightDoors = self.ui.rightDoors.isChecked()
            self.openDoorsManual()
        if (self.autoMode):
            self.ui.exteriorLights.setChecked(self.exteriorLightsOn)
        self.ui.upcomingStation.setPlainText(str(self.station))
        self.ui.encodedBeacon.setPlainText(str(bin(self.encodedBeacon)))
        self.ui.power.display(self.power / 1000)
        self.ui.textBrowser_17.setPlainText(str(int(self.power)))
        if (self.upcomingStation):
            self.ui.upcomingStation.setStyleSheet(
                u"background-color: rgb(124,252,0);")
            #going through our arrival procedures
            if (self.alreadyWaiting == False):
                self.onArrival()
        else:
            self.ui.upcomingStation.setStyleSheet(
                u"background-color: rgb(255,255,255);")

    #onEmergencyBrake: used to enable emergency brake
    def onEmergencyBrake(self):
        print("Emergency Brake Activated")
        self.setpointSpeed = 0
        self.power = 0
        self.emergencyBrake = True
        self.displayUpdate()

    #onEmergencyBrakeOff: used to disable emergency brake upon release
    def onEmergencyBrakeOff(self):
        print("Emergency Brake Released")
        self.emergencyBrake = False
        self.displayUpdate()

    #serviceBrakeActivated: used when service Brake activated
    def serviceBrakeActivated(self):
        self.serviceBrake = True
        print("Service Brake Activated")
        self.setPointSpeed = 0
        self.power = 0
        self.displayUpdate()

    #serviceBrakeDeactivated: used when service brake released
    def serviceBrakeDeactivated(self):
        self.serviceBrake = False
        print("Service Brake Released")
        self.displayUpdate()

    #onPassengerBrakeActivated: act upon passenger brake
    def onPassengerBrakeActivated(self):
        print("Passenger Brake Activated!")
        self.ui.textBrowser_16.setStyleSheet(
            u"background-color: rgb(255, 0, 0);")
        self.emergencyBrake = True
        self.setPointSpeed = 0
        self.commandedSpeed = 0

    #onUpdateTCFake: used to act upon TC Values
    def onUpdateTCFake(self):
        self.commandedSpeed = (2.237 *
                               float(self.ui.inputCommanded.toPlainText()))
        self.authority = float(
            float(self.ui.inputAuthority.toPlainText()) * 3.28084)

        print("Fake Commanded" + str(self.commandedSpeed))
        print("Fake Authority" + str(self.authority))

        if (self.authority <= 0):
            self.onAuthorityExpiration()

        #encoding the track circuit stuff
        cmdInt = int(float(self.commandedSpeed))
        cmdFloat = int(((float(self.commandedSpeed) - cmdInt) * 10))
        authInt = int(float(self.authority))
        authFloat = int(((float(self.authority) - authInt) * 10))

        if (self.ui.causePickupFailure.checkState()):
            self.encodedTC = (cmdInt - 6 & 255)
        else:
            self.encodedTC = (cmdInt & 255)
        self.encodedTC += (cmdFloat & 15) << 8
        self.encodedTC += (authInt & 255) << 12
        self.encodedTC += (authFloat & 15) << 20
        self.encodedTC += (
            (cmdInt + cmdFloat + authFloat + authInt) & 1023) << 24

        print(self.encodedTC)
        self.decodeTC()

        #updating the display
        self.displayUpdate()

    #onUpdateActualFake: used for updating current speed
    def onUpdateActualFake(self):
        self.actualSpeedSecond = self.actualSpeedLast
        self.actualSpeedLast = self.actualSpeed
        self.actualSpeed = (2.237 * float(self.ui.actualSpeed.toPlainText()))

        #checking to see if there is train engine failure
        self.detectEngineFailure()
        self.detectBrakeFailure()
        self.displayUpdate()

    #onBrakeFailure: used to detect engine failures
    def detectEngineFailure(self):
        #defined by Train Model
        #stephen send a value as our current velocity symbolizing problem with engine
        if (self.actualSpeed == 666):
            self.onEngineFailure()

    #causeEngineFailure: used to cause engine failures
    def causeEngineFailure(self):
        self.actualSpeed = 666
        self.detectEngineFailure()

    #onEngineFailure: used to act upon engine failures
    def onEngineFailure(self):
        self.ui.textBrowser_13.setStyleSheet(
            u"background-color: rgb(255, 0, 0);")
        self.emergencyBrake = true

    #detectBrakeFailure: used to detect brake failures
    def detectBrakeFailure(self):
        # a brake failure is actually when the brakes are stuck on, so if we're slowing down, no brakes are applied and power is not 0
        #print("Brake Failure Detection Occuring")
        #print("Service Brake: " + str(self.serviceBrake))
        if (not self.serviceBrake
                and (self.actualSpeedSecond > self.actualSpeedLast
                     and self.actualSpeedLast > self.actualSpeed)
                and not self.power == 0):
            self.onBrakeFailure()

    #causeBrakeFailure: used to simulate brake failures
    def causeBrakeFailure(self):
        self.actualSpeedSecond = 17
        self.actualSpeedLast = 16
        self.actualSpeed = 15
        self.serviceBrake = False
        self.detectBrakeFailure()

    #onBrakeFailure: used to act on brake failures
    def onBrakeFailure(self):
        self.ui.textBrowser_14.setStyleSheet(
            u"background-color: rgb(255, 0, 0);")
        self.emergencyBrake = True

    #onUpdateBeaconFake: Used to update beacon values
    def onUpdateBeaconFake(self):
        beaconNum = self.ui.stationNameFake.currentIndex()
        #encoding my beacon
        self.encodedBeacon = int(self.ui.stationUpcoming.checkState()) >> 1
        print(bin(self.encodedBeacon))
        self.encodedBeacon += (
            int(self.ui.leftDoorsFake.checkState()) >> 1) << 1
        self.encodedBeacon += (
            int(self.ui.rightDoorsFake.checkState()) >> 1) << 2
        self.encodedBeacon += (
            int(self.ui.exteriorLightsFake.checkState()) >> 1) << 3
        self.encodedBeacon += (beaconNum & 31) << 4
        print(bin(self.encodedBeacon))
        self.displayUpdate()
        self.decodeBeacon()

    #decodeBeacon: Used to decode beacon
    def decodeBeacon(self):
        self.upcomingStation = (self.encodedBeacon & 1)
        self.leftDoorsOpen = (self.encodedBeacon >> 1) & 1
        self.rightDoorsOpen = (self.encodedBeacon >> 2) & 1
        self.exteriorLightsOn = (self.encodedBeacon >> 3) & 1
        self.station = self.stationArray[((self.encodedBeacon >> 4) & 31)]
        self.buildAnnouncement()
        if (self.autoMode):
            self.onAnnouncement()
        self.displayUpdate()

    #buildAnnouncement: Used to build announcement
    def buildAnnouncement(self):
        if (self.upcomingStation):
            self.announcement = str("Arriving at " + self.station +
                                    " Station. The doors will open on the ")
            if (self.leftDoorsOpen and self.rightDoorsOpen):
                self.announcement += "left and right.\n"
            elif (self.leftDoorsOpen):
                self.announcement += "left.\n"
            else:
                self.announcement += "right.\n"
        else:
            self.announcement = ""

    #onUpdateKpKi: Used to update Kp/Ki Values
    def onUpdateKpKi(self):
        self.pid.Ki = float(self.ui.kiInput.toPlainText())
        self.pid.Kp = float(self.ui.kpInput.toPlainText())
        print("Kp Ki values updated" + str(self.pid.Ki) + str(self.pid.Kp))

    #checkSignalPickup: Used to detect and act on signal pickup failures
    def checkSignalPickup(self, tempCmdInt, tempCmdFloat, tempAuthInt,
                          tempAuthFloat, tempCheckSum):
        if (tempCheckSum !=
                tempCmdInt + tempCmdFloat + tempAuthInt + tempAuthFloat):
            print("Signal Pickup Failure")
            self.ui.textBrowser_15.setStyleSheet(
                u"background-color: rgb(255, 0, 0);")
            self.emergencyBrake = True
        else:
            self.ui.textBrowser_15.setStyleSheet(
                u"background-color: rgb(255, 255, 255);")

    #decodeTC: Used to decode track circuit
    def decodeTC(self):
        tempCmdInt = self.encodedTC & 255
        tempCmdFloat = (self.encodedTC >> 8) & 15
        tempAuthInt = (self.encodedTC >> 12) & 255
        tempAuthFloat = (self.encodedTC >> 20) & 15
        tempCheckSum = (self.encodedTC >> 24) & 1023
        self.checkSignalPickup(tempCmdInt, tempCmdFloat, tempAuthInt,
                               tempAuthFloat, tempCheckSum)
        #if((tempAuthInt == 0 and tempAuthFloat == 0)):
        #self.onAuthorityExpiration()

    #onAuthorityExpiration: Used as emergency procedure if authority has expired
    def onAuthorityExpiration(self):
        print("Authority Expired")
        self.setPointSpeed = 0
        self.commandedSpeed = 0
        self.emergencyBrake = True
        self.ui.textBrowser_5.setStyleSheet(u"background-color: rgb(255,0,0);")

    #pidLoop: used to calculate power
    def pidLoop(self):
        #print("INPID")
        print("auto mode = " + str(self.autoMode))
        print("self.upComingStation = " + str(self.upcomingStation))
        print("service brake" + str(self.serviceBrake))
        print("emergency brake" + str(self.emergencyBrake))
        if (self.autoMode and not self.upcomingStation
                and ((not self.serviceBrake) and (not self.emergencyBrake))):
            print("PID Auto")
            self.pid.setpoint = self.commandedSpeed
            self.power = self.pid(self.actualSpeed, dt=1)
        elif (not (self.setPointSpeed == 0) and not self.serviceBrake
              and not self.emergencyBrake
              and not self.ui.leftDoors.isChecked()
              and not self.ui.rightDoors.isChecked()):
            print("PID Manual")
            self.pid.setpoint = self.setPointSpeed
            self.power = self.pid(self.actualSpeed, dt=1)
        else:
            self.pid.setpoint = 0
            self.power = 0
            print("inPIDOff")
        self.displayUpdate()
        # print(self.power)

    #onAnnouncement: Used to display announcement
    def onAnnouncement(self):
        self.ui.announcementDisplay.setPlainText(str(self.announcement))
        self.ui.intercomFake.setPlainText(str(self.announcement))

    #onAutoManual: Used to differentiate between operation modes
    def onAutoManual(self):
        if (self.ui.automaticMode.isChecked()):
            self.autoMode = True
            print("Auto Mode Entered")

            #making the door checkboxes not usable
            self.ui.leftDoors.setEnabled(False)
            self.ui.rightDoors.setEnabled(False)

        else:
            self.autoMode = False
            print("Manual Mode Entered")

            #making the door checkboxes usable
            self.ui.leftDoors.setEnabled(True)
            self.ui.rightDoors.setEnabled(True)

    def onArrival(self):
        #if we are in autoMode, and we have upComingStation = true, stop the train
        if (self.autoMode == True):
            self.alreadyWaiting = True
            self.previousCommanded = self.commandedSpeed
            self.serviceBrake = True
            self.commandedSpeed = 0
            self.power = 0

            self.waitTimer = QTimer()
            self.waitTimer.timeout.connect(self.checkVel)
            self.waitTimer.start(1000)

        else:
            ...
            #Letting use open the doors on their own

    #openDoorsManual: used in Manual Mode to open/close doors
    def openDoorsManual(self):
        if (self.autoMode == False):
            if (self.actualSpeed == 0):
                if (self.leftDoors):
                    self.ui.leftDoorsStatus.setPlainText("Open")
                    self.ui.leftDoorsStatus.setStyleSheet(
                        u"background-color: rgb(124,252,0);")

                if (self.rightDoors):
                    self.ui.rightDoorsStatus.setPlainText("Open")
                    self.ui.rightDoorsStatus.setStyleSheet(
                        u"background-color: rgb(124,252,0);")
                    self.openedr = True
            if (not self.leftDoors):
                self.ui.leftDoorsStatus.setPlainText("Closed")
                self.ui.leftDoorsStatus.setStyleSheet(
                    u"background-color: rgb(255,255,255);")
            if (not self.rightDoors):
                self.ui.rightDoorsStatus.setPlainText("Closed")
                self.ui.rightDoorsStatus.setStyleSheet(
                    u"background-color: rgb(255,255,255);")

    #checkVel: Used in Auto mode to see if doors can be opened
    def checkVel(self):
        if (self.actualSpeed == 0):
            self.waitTimer.stop()
            self.upcomingStation = False
            self.ui.stationUpcoming.setChecked(False)
            self.openDoors()

    #openDoors: Used in Auto mode to open doors
    def openDoors(self):
        #opening the doors
        if (self.leftDoorsOpen):
            self.ui.leftDoorsStatus.setPlainText("Open")
            self.ui.leftDoorsStatus.setStyleSheet(
                u"background-color: rgb(124,252,0);")
        if (self.rightDoorsOpen):
            self.ui.rightDoorsStatus.setPlainText("Open")
            self.ui.rightDoorsStatus.setStyleSheet(
                u"background-color: rgb(124,252,0);")

        self.waitForDisembark()

    #waitForDisembark: Used in Auto mode to close doors
    def waitForDisembark(self):
        #waiting for passengers to disembark
        self.waitTimer2 = QTimer()
        self.waitTimer2.timeout.connect(self.closeDoors)
        self.waitTimer2.start(4000)
        #where you would send out a signal to close doors

    #closeDoors: Used in Auto mode to close doors and keep going
    def closeDoors(self):
        self.waitTimer2.stop()
        print("Done with disembarking.")
        self.ui.stationUpcoming.setChecked(False)
        self.upcomingStation = False
        self.ui.rightDoorsStatus.setPlainText("Closed")
        self.ui.leftDoorsStatus.setPlainText("Closed")
        self.ui.leftDoorsStatus.setStyleSheet(
            u"background-color: rgb(255,255,255);")
        self.ui.rightDoorsStatus.setStyleSheet(
            u"background-color: rgb(255,255,255);")
        self.leftDoorsOpen = False
        self.rightDoorsOpen = False
        self.commandedSpeed = self.previousCommanded
        self.alreadyWaiting = False
        self.serviceBrake = False
        print("upcomingStation flag is" + str(self.upcomingStation))

    def onTemperatureChange(self):
        self.temperature = int(self.ui.temperature.value())
        print("Temp is " + str(self.temperature))
        self.ui.textBrowser_12.setPlainText(str(self.temperature))
        self.displayUpdate()

    def onExtLightsChanged(self):
        self.ui.exteriorLights_2.setChecked(
            self.ui.exteriorLights.checkState())
        self.displayUpdate()

    def onIntLightsChanged(self):
        self.ui.interiorLights_2.setChecked(
            self.ui.interiorLights.checkState())
        self.displayUpdate()
Beispiel #14
0
class SimpleTimer(QWidget, BaseTimer):

    paused = Signal()

    def __init__(self, parent=None):

        super().__init__(parent)
        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.update_timer = QTimer(self)
        self.setting_time = 0
        self.remaining = 0

        self.remain_label = QLabel('00:00', self)
        self.remain_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.remain_label.setScaledContents(True)
        font = QFont()
        font.setPointSize(86)
        self.remain_label.setFont(font)
        self.timer_edit = QSpinBox(self)
        self.timer_edit.setRange(1, 99)
        self.timer_edit.setValue(25)

        self.set_ui()
        self.set_connection()

        self.show()

    def set_ui(self):
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.remain_label, alignment=Qt.AlignHCenter)
        vlayout.addWidget(self.timer_edit)
        self.setLayout(vlayout)

    def set_connection(self):
        self.timer.timeout.connect(self.stop)
        self.update_timer.timeout.connect(self.remain_update)

    def start(self):
        self.setting_time = self.timer_edit.value()
        self.timer.start(self.setting_time * 60 * 1000)
        self.set_remain_update()
        self.started.emit()

    def set_remain_update(self):
        self.remain_update()
        self.update_timer.start(250)

    def stop(self):
        self.reset()
        self.finished.emit()

    def abort(self):
        self.reset()
        self.timer.stop()
        self.aborted.emit()

    def pause(self):
        self.update_timer.stop()
        self.remaining = self.timer.remainingTime()
        self.timer.stop()
        self.paused.emit()

    def resume(self):
        self.timer.start(self.remaining)
        self.set_remain_update()
        self.started.emit()

    def reset(self):
        self.timer.stop()
        self.update_timer.stop()
        self.remain_label.setText('00:00')
        self.remaining = 0

    def get_notify_message(self):
        remaining = self.timer.remainingTime() / 1000
        return '{0} minutes have passed.'.format(self.setting_time - int(remaining/60))

    def remain_update(self):
        remaining = self.timer.remainingTime() / 1000
        self.remain_label.setText('{min:02}:{sec:02}'.format(
            min=int(remaining / 60), sec=int(remaining % 60)))

    @property
    def name(self):
        return 'Simple Timer'
class AudioTest(QMainWindow):

    PUSH_MODE_LABEL = "Enable push mode"
    PULL_MODE_LABEL = "Enable pull mode"
    SUSPEND_LABEL = "Suspend playback"
    RESUME_LABEL = "Resume playback"

    DurationSeconds = 1
    ToneSampleRateHz = 600
    DataSampleRateHz = 44100

    def __init__(self):
        super(AudioTest, self).__init__()

        self.m_device = QAudioDeviceInfo.defaultOutputDevice()
        self.m_output = None

        self.initializeWindow()
        self.initializeAudio()

    def initializeWindow(self):
        layout = QVBoxLayout()

        self.m_deviceBox = QComboBox()
        self.m_deviceBox.activated[int].connect(self.deviceChanged)
        for deviceInfo in QAudioDeviceInfo.availableDevices(
                QAudio.AudioOutput):
            self.m_deviceBox.addItem(deviceInfo.deviceName(), deviceInfo)

        layout.addWidget(self.m_deviceBox)

        self.m_modeButton = QPushButton()
        self.m_modeButton.clicked.connect(self.toggleMode)
        self.m_modeButton.setText(self.PUSH_MODE_LABEL)

        layout.addWidget(self.m_modeButton)

        self.m_suspendResumeButton = QPushButton(
            clicked=self.toggleSuspendResume)
        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

        layout.addWidget(self.m_suspendResumeButton)

        volumeBox = QHBoxLayout()
        volumeLabel = QLabel("Volume:")
        self.m_volumeSlider = QSlider(Qt.Horizontal,
                                      minimum=0,
                                      maximum=100,
                                      singleStep=10)
        self.m_volumeSlider.valueChanged.connect(self.volumeChanged)

        volumeBox.addWidget(volumeLabel)
        volumeBox.addWidget(self.m_volumeSlider)

        layout.addLayout(volumeBox)

        window = QWidget()
        window.setLayout(layout)

        self.setCentralWidget(window)

    def initializeAudio(self):
        self.m_pullTimer = QTimer(self)
        self.m_pullTimer.timeout.connect(self.pullTimerExpired)
        self.m_pullMode = True

        self.m_format = QAudioFormat()
        self.m_format.setSampleRate(self.DataSampleRateHz)
        self.m_format.setChannelCount(1)
        self.m_format.setSampleSize(16)
        self.m_format.setCodec('audio/pcm')
        self.m_format.setByteOrder(QAudioFormat.LittleEndian)
        self.m_format.setSampleType(QAudioFormat.SignedInt)

        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if not info.isFormatSupported(self.m_format):
            qWarning("Default format not supported - trying to use nearest")
            self.m_format = info.nearestFormat(self.m_format)

        self.m_generator = Generator(self.m_format,
                                     self.DurationSeconds * 1000000,
                                     self.ToneSampleRateHz, self)

        self.createAudioOutput()

    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)

    def deviceChanged(self, index):
        self.m_pullTimer.stop()
        self.m_generator.stop()
        self.m_audioOutput.stop()
        self.m_device = self.m_deviceBox.itemData(index)

        self.createAudioOutput()

    def volumeChanged(self, value):
        if self.m_audioOutput is not None:
            self.m_audioOutput.setVolume(value / 100.0)

    def notified(self):
        qWarning(
            "bytesFree = %d, elapsedUSecs = %d, processedUSecs = %d" %
            (self.m_audioOutput.bytesFree(), self.m_audioOutput.elapsedUSecs(),
             self.m_audioOutput.processedUSecs()))

    def pullTimerExpired(self):
        if self.m_audioOutput is not None and self.m_audioOutput.state(
        ) != QAudio.StoppedState:
            chunks = self.m_audioOutput.bytesFree(
            ) // self.m_audioOutput.periodSize()
            for _ in range(chunks):
                data = self.m_generator.read(self.m_audioOutput.periodSize())
                if data is None or len(
                        data) != self.m_audioOutput.periodSize():
                    break

                self.m_output.write(data)

    def toggleMode(self):
        self.m_pullTimer.stop()
        self.m_audioOutput.stop()

        if self.m_pullMode:
            self.m_modeButton.setText(self.PULL_MODE_LABEL)
            self.m_output = self.m_audioOutput.start()
            self.m_pullMode = False
            self.m_pullTimer.start(20)
        else:
            self.m_modeButton.setText(self.PUSH_MODE_LABEL)
            self.m_pullMode = True
            self.m_audioOutput.start(self.m_generator)

        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

    def toggleSuspendResume(self):
        if self.m_audioOutput.state() == QAudio.SuspendedState:
            qWarning("status: Suspended, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.ActiveState:
            qWarning("status: Active, suspend()")
            self.m_audioOutput.suspend()
            self.m_suspendResumeButton.setText(self.RESUME_LABEL)
        elif self.m_audioOutput.state() == QAudio.StoppedState:
            qWarning("status: Stopped, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.IdleState:
            qWarning("status: IdleState")

    stateMap = {
        QAudio.ActiveState: "ActiveState",
        QAudio.SuspendedState: "SuspendedState",
        QAudio.StoppedState: "StoppedState",
        QAudio.IdleState: "IdleState"
    }

    def handleStateChanged(self, state):
        qWarning("state = " + self.stateMap.get(state, "Unknown"))
Beispiel #16
0
class TapList(QMainWindow):
    def __init__(self, parent=None):
        # init
        super(TapList, self).__init__(parent)
        self.infoOutput()
        self.loadConfig()
        self.initUI()
        self.initLayout()
        self.makeTapList(getFrom=self.data_source, menuSide=self.side)
        self.createContextMenu()
        # start renew timer  
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.renewTimer)
        self.startTimer()

    # load config parameter
    def loadConfig(self):
        self.config = ConfigParser()
        self.config.read('config.ini')
        self.window_title = self.config.get('default', 'window_title')
        self.side = int(self.config.get('default', 'side'))
        self.renew_period = int(self.config.get('default', 'renew_period'))
        self.data_source = int(self.config.get('default', 'data_source'))
        self.notic_0 = self.config.get('default', 'notic_0')
        self.notic_1 = self.config.get('default', 'notic_1')
        self.logo_path = self.config.get('default', 'logo_path')

    # setup GUI
    def initUI(self):
        # setup UI
        self.setWindowTitle("Taplist - Let's Beer Brewpub")
        # get screen width and height
        self.screen = QGuiApplication.primaryScreen().geometry()
        self.width = self.screen.width()
        self.height = self.screen.height()
        # set screen size
        self.resize(1920, 1080)
        # self.setMinimumWidth(800)
        self.layout = QGridLayout()
    
    # setup layout
    def initLayout(self):
        self.layout = QGridLayout()
        self.window = QWidget()
        self.window.setLayout(self.layout)

        # set header(logo) layout
        self.layoutH = QLabel()
        self.layoutH.setObjectName('header')
        self.logo = QPixmap('img/logo_w.png')
        self.layoutH.setPixmap(self.logo)
        self.layoutH.setFixedHeight(100)
        self.layoutH.setAlignment(Qt.AlignCenter)

        # set tap list item layout
        self.layout0 = QLabel()
        self.layout0.setObjectName('itme_0')
        self.layout4 = QLabel()
        self.layout4.setObjectName('itme_4')
        self.layout1 = QLabel()
        self.layout1.setObjectName('itme_1')
        self.layout5 = QLabel()
        self.layout5.setObjectName('itme_5')
        self.layout2 = QLabel()
        self.layout2.setObjectName('itme_2')
        self.layout6 = QLabel()
        self.layout6.setObjectName('itme_6')
        self.layout3 = QLabel()
        self.layout3.setObjectName('itme_3')
        self.layout7 = QLabel()
        self.layout7.setObjectName('itme_7')
        self.layout8 = QLabel()                 # splitter line
        self.layout8.setObjectName('splitter')
        self.layout8.setFixedWidth(3)

        # set footer(message bar) layout
        self.layoutF = QLabel()
        self.layoutF.setObjectName('footer')
        self.layoutF.setFixedHeight(120)
        self.layoutF.setAlignment(Qt.AlignCenter)
        if self.side==0:
            notic_text = self.notic_0
        else:
            notic_text = self.notic_1
        self.layoutF.setText(notic_text)

        # gridding
        self.layout.addWidget(self.layoutH, 0, 0, 1, 3)
        self.layout.addWidget(self.layout0, 1, 0)
        self.layout.addWidget(self.layout4, 1, 2)
        self.layout.addWidget(self.layout1, 2, 0)
        self.layout.addWidget(self.layout5, 2, 2)
        self.layout.addWidget(self.layout2, 3, 0)
        self.layout.addWidget(self.layout6, 3, 2)
        self.layout.addWidget(self.layout3, 4, 0)
        self.layout.addWidget(self.layout7, 4, 2)
        self.layout.addWidget(self.layout8, 1, 1, 4, 1)
        self.layout.addWidget(self.layoutF, 5, 0, 1, 3)
        self.setCentralWidget(self.window)

    def draw(self, targetLayout, itemData):
        self.layoutItemDetail = QGridLayout(parent=targetLayout)

        self.layoutItem_no      = QLabel()
        self.layoutItem_no.setObjectName('layoutItem_no')
        self.layoutItem_no.setAlignment(Qt.AlignCenter)
        self.layoutItem_namecn  = QLabel()
        self.layoutItem_namecn.setObjectName('layoutItem_namecn')
        self.layoutItem_namecn.setAlignment(Qt.AlignBottom)
        self.layoutItem_nameen  = QLabel()
        self.layoutItem_nameen.setObjectName('layoutItem_nameen')
        self.layoutItem_data    = QLabel()
        self.layoutItem_data.setObjectName('layoutItem_data')
        self.layoutItem_data.setAlignment(Qt.AlignTop)
        self.layoutItem_price   = QLabel()
        self.layoutItem_price.setObjectName('layoutItem_price')
        self.layoutItem_price.setAlignment(Qt.AlignBottom | Qt.AlignCenter)
        self.layoutItem_capacity = QLabel()
        self.layoutItem_capacity.setObjectName('layoutItem_capacity')
        self.layoutItem_capacity.setAlignment(Qt.AlignCenter | Qt.AlignTop)

        self.layoutItemDetail.addWidget(self.layoutItem_no,    0, 0, 4,  2)
        self.layoutItemDetail.addWidget(self.layoutItem_namecn,0, 3, 2, 12)
        self.layoutItemDetail.addWidget(self.layoutItem_nameen,2, 3, 1, 12)
        self.layoutItemDetail.addWidget(self.layoutItem_data,  3, 3, 1, 12)
        self.layoutItemDetail.addWidget(self.layoutItem_price, 0, 15, 2, 2)
        self.layoutItemDetail.addWidget(self.layoutItem_capacity, 2, 15, 2, 2)

        self.write2Layout(itemData)
    
    def write2Layout(self, itemData):
        tapnum = ['0', '1', '2', '3', '4', '5', '6',
                  '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
        self.layoutItem_no.setText('#' + tapnum[int(itemData['tapid'])])
        self.layoutItem_namecn.setText(
            itemData['brewery'] + ' ' + itemData['beername'] + ' ' + itemData['beerstyle'])
        self.layoutItem_nameen.setText(itemData['beernameen'])
        self.layoutItem_data.setText(
            '酒精度 ' + itemData['abv'] + '% ABV   苦度 ' + itemData['ibu'] + ' IBU')
        self.layoutItem_price.setText('¥' + itemData['price'])
        self.layoutItem_capacity.setText(
            '杯型<br>' + itemData['glass_type'] + 'mL')

    def makeTapList(self, getFrom, menuSide=0):
        # get data
        self.data = self.getData(getdatafrom=getFrom, side=menuSide)
        # draw
        for i in range(8):
            exec('self.draw(targetLayout=self.layout%s,itemData=self.data[i])'%str(i))
            # check item status
            if self.data[i]['status'] == '1':
                exec('self.layout%s.setStyleSheet("color: #373737; border: #373737; ")' % str(i))
                
    def getData(self, getdatafrom, side):
        # get data from: 
        # 0-test json data
        # 1-wechat interface\
        # 2-dragon head database
        data = []
        if getdatafrom==0:
            # load data from json file
            with open("sandbox/tapdata.json", "r") as f:
                data = json.load(f)
            if side==1:
                data = data[8:]
        elif getdatafrom==1:
            data = self.getDataFromWx(side=side)
        return data

    def getDataFromWx(self,side):
        data = []
        token = self.wx_get_access_token()
        # tap info
        if side == 0:
            query_str = '''
            db.collection("tapinfo").where({'tapid':_.lt(8)}).orderBy(
                'tapid', 'asc').limit(8).get()
            '''
        elif side == 1:
            query_str = '''
            db.collection("tapinfo").where({'tapid':_.gte(8)}).orderBy('tapid', 'asc').limit(8).get()
            '''
        try:
            query_result = self.wx_query_data(token=token, query=query_str)
        except:
            data = self.data
            print('err_log:get query data from wx failed.')
        else:
            for row in query_result:
                r = json.loads(row)
                if r['status'] == True:
                    status = '0'
                elif r['status'] == False:
                    status = '1'
                data.append({
                    'tapid': str(r['tapid']),
                    'brewery': r['brewery'],
                    'beername': r['beername'],
                    'beerstyle': r['beerstyle'],
                    'beernameen': r['ebeername'],
                    'abv': r['abv'],
                    'ibu': r['ibu'],
                    'price': r['price'],
                    'glass_type': r['glass_type'],
                    'country': r['country'],
                    'status': status
                })
        return data

    def wx_get_access_token(self):
        cs_url = 'https://api.weixin.qq.com/cgi-bin/token?'
        param = {
            'grant_type': 'client_credential',
            'appid': self.config.get('wx', 'appid'),
            'secret': self.config.get('wx', 'secret')
        }
        headers = {'Accept': 'application/json'}
        try:
            r = requests.get(cs_url, params=param, headers=headers)
        except:
            print('err_log:get access token failed.')
        else:
            data = json.loads(r.text)
            if 'errcode' in data:
                print(data['errmsg'])
            else:
                return data['access_token']

    def wx_get_collection(self, token):
        cs_url = 'https://api.weixin.qq.com/tcb/databasecollectionget?'
        params = {
            'access_token': token
        }
        body = {
            'limit': '10',
            'offset': '0',
            'env': self.config.get('wx', 'envid')
        }
        headers = {'content-type': 'application/json'}
        try:
            r = requests.post(cs_url, params=params,
                            data=json.dumps(body), headers=headers)
        except:
            return self.data
        else:
            data = json.loads(r.text)
            
        if data['errcode'] == 0:
            return data['collections']
        else:
            return data['errmsg']

    def wx_query_data(self, token, query):
        cs_url = 'https://api.weixin.qq.com/tcb/databasequery?'
        params = {
            'access_token': token
        }
        body = {
            'env': self.config.get('wx', 'envid'),
            'query': query
        }
        headers = {'content-type': 'application/json'}
        try:
            r = requests.post(cs_url, params=params,
                data=json.dumps(body), headers=headers)
        except:
            return self.data
        else:
            data = json.loads(r.text)
            if data['errcode'] == 0:
                return data['data']
            else:
                return data['errmsg']
    
    # remake menu (update/switch)
    def reNewMenu(self):
        self.window.setParent(None)
        self.initLayout()
        self.makeTapList(getFrom=self.data_source, menuSide=self.side)
        print(str(datetime.datetime.now()) + 
            ' ==> refresh taplist side: ' + str(self.side) + 
            ' from: ' + str(self.data_source)
        )
    
    def renewTimer(self):
        self.endTimer()
        self.reNewMenu()
        self.startTimer()
    
    def startTimer(self):
        self.timer.start(self.renew_period)  # 5000 单位是毫秒, 即 5 秒

    def endTimer(self):
        self.timer.stop()
    
    # creat menu and bind action
    def createContextMenu(self):
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        self.contextMenu = QMenu(self)
        self.actionSWitch = self.contextMenu.addAction(u'Switch')
        self.actionRefresh = self.contextMenu.addAction(u'Refresh')

        self.actionSWitch.triggered.connect(self.switchSide)
        self.actionRefresh.triggered.connect(self.reNewMenu)
    
    # show menu
    def showContextMenu(self, pos):
        self.contextMenu.move(QCursor().pos())
        self.contextMenu.show()
    
    # menu action switch menu side
    def switchSide(self):
        if self.side == 0:
            self.side = 1
        elif self.side == 1:
            self.side = 0
        self.reNewMenu()

    # output information
    def infoOutput(self):
        print(datetime.datetime.now())
        print("Python version is " + sys.version)
        print("PySide6 version is " + PySide6.__version__)
Beispiel #17
0
class PomoTimer(QWidget, BaseTimer):
    """
    PomoTimer: Pomodoro タイマーを提供する。

    1 pomodoro: 25min
    short break: 5min
    long break: 15min
    long break after 4pomodoro.
    """

    paused = Signal()

    def __init__(self):
        super().__init__()
        self.simple_timer = SimpleTimer(self)

        self.settings = {
            'pomo': 25,
            'short_break': 5,
            'long_break': 15,
            'long_break_after': 4
        }
        self.state_dict = {
            'work': 'Work!',
            'break': 'Break.',
            'pause': 'Pause',
            'wait': "Let's start."
        }
        self.work_timer = QTimer(self)
        self.work_timer.setSingleShot(True)
        self.break_timer = QTimer(self)
        self.break_timer.setSingleShot(True)

        self.pomo_count = 0
        self.pomo_count_lbl = QLabel(self)
        self.pomo_count_lbl.setText(f'{self.pomo_count} pomodoro finished.')
        self.state_lbl = QLabel(self)
        self.state_lbl.setText(self.state_dict['wait'])
        self.estimate_pomo = 0
        self.estimate_label = QLabel(self)
        self.estimate_label.setText('Estimate: ')
        self.estimate_pomo_widget = QSpinBox(self)
        self.estimate_pomo_widget.setValue(4)
        self.estimate_pomo_widget.setSuffix('  pomo')
        self.estimate_pomo_widget.setRange(1, 20)

        self.set_ui()
        self.set_connection()

    def set_ui(self):
        layout = self.simple_timer.layout()
        i = layout.indexOf(self.simple_timer.timer_edit)
        layout.takeAt(i)
        hlayout = QHBoxLayout()
        hlayout.addWidget(self.estimate_label)
        hlayout.addWidget(self.estimate_pomo_widget)

        vlayout = QVBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addWidget(self.pomo_count_lbl)
        vlayout.addWidget(self.state_lbl)
        vlayout.addWidget(self.simple_timer)
        self.setLayout(vlayout)

    def set_connection(self):
        self.work_timer.timeout.connect(self.timeout)
        self.break_timer.timeout.connect(self.start_work)

    def start(self):
        self.estimate_pomo = self.estimate_pomo_widget.value()
        self.start_work()

    def start_work(self):
        """
        Start timer for working on the task.
        :return:
        """
        self.state_lbl.setText(self.state_dict['work'])
        self.simple_timer.timer = self.work_timer
        self.simple_timer.timer_edit.setValue(self.settings['pomo'])
        self.simple_timer.start()
        self.started.emit()

    def start_break(self):
        """
        Start timer for working on the rest.
        Short break is normal break, long break comes every some tasks(default 4).
        :return:
        """
        self.state_lbl.setText(self.state_dict['break'])
        self.simple_timer.timer = self.break_timer
        if self.pomo_count % self.settings['long_break_after']:
            self.simple_timer.timer_edit.setValue(self.settings['short_break'])
        else:
            self.simple_timer.timer_edit.setValue(self.settings['long_break'])
        self.simple_timer.start()
        self.started.emit()

    def abort(self):
        self.reset()
        self.aborted.emit()

    def pause(self):
        self.state_lbl.setText(self.state_dict['pause'])
        self.simple_timer.pause()
        self.paused.emit()

    def resume(self):
        self.state_lbl.setText(self.state_dict['work'])
        self.simple_timer.resume()
        self.started.emit()

    def timeout(self):
        self.pomo_count += 1
        if self.pomo_count >= self.estimate_pomo:
            self.reset()
            self.finished.emit()
        else:
            self.start_break()

    def reset(self):
        self.pomo_count = 0
        self.simple_timer.reset()
        self.work_timer.stop()
        self.break_timer.stop()
        self.state_lbl.setText(self.state_dict['wait'])

    def get_notify_message(self):
        return ''

    @property
    def name(self):
        return 'Pomo Timer'

    def fake_start(self):
        self.simple_timer.setting_time = self.simple_timer.timer_edit.value()
        self.simple_timer.timer.start(self.simple_timer.setting_time * 1000)
        self.simple_timer.set_remain_update()
        self.simple_timer.started.emit()
Beispiel #18
0
class ModList(QTableView):
    def __init__(self, parent: QWidget, model: Model) -> None:
        super().__init__(parent)

        settings = QSettings()

        self.hoverIndexRow = -1
        self.modmodel = model
        self.installLock = asyncio.Lock()

        self.setMouseTracking(True)
        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setWordWrap(False)
        self.setSortingEnabled(True)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setAcceptDrops(True)
        self.setEditTriggers(QTableView.EditKeyPressed
                             | QTableView.DoubleClicked)
        self.setShowGrid(False)

        self.setStyleSheet('''
            QTableView {
                gridline-color: rgba(255,255,255,1);
            }
            QTableView::item:!selected:hover {
                background-color: rgb(217, 235, 249);
            }
            ''')

        self.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.showContextMenu)

        self.verticalHeader().hide()
        self.verticalHeader().setVisible(False)
        self.setSectionSize(settings.value('compactMode', 'False') == 'True')

        self.setCornerButtonEnabled(False)
        self.horizontalHeader().setHighlightSections(False)
        self.horizontalHeader().setStretchLastSection(True)
        self.horizontalHeader().setSectionsMovable(True)

        self.listmodel = ModListModel(self, model)
        self.filtermodel = ModListFilterModel(self, self.listmodel)
        self.setModel(self.filtermodel)

        self.setItemDelegate(ModListItemDelegate(self))
        self.setSelectionModel(ModListSelectionModel(self, self.filtermodel))

        if len(model):
            self.modCountLastUpdate = len(model)
            self.resizeColumnsToContents()
        else:
            self.modCountLastUpdate = -1

        if settings.value('modlistHorizontalHeaderState'):
            self.horizontalHeader().restoreState(
                settings.value('modlistHorizontalHeaderState'))  # type: ignore

        self.horizontalHeader().sectionMoved.connect(
            lambda: self.headerChangedEvent())
        self.horizontalHeader().sectionResized.connect(
            lambda: self.headerChangedEvent())

        self.setFocus()

        self.sortByColumn(3, Qt.AscendingOrder, False)
        self.sortByColumn(2, Qt.AscendingOrder, False)
        self.sortByColumn(1, Qt.AscendingOrder, False)
        if settings.value('modlistSortColumn') is not None and \
           settings.value('modlistSortOrder') is not None:
            try:
                self.sortByColumn(
                    cast(int, settings.value('modlistSortColumn', 1,
                                             int)), Qt.DescendingOrder
                    if cast(int, settings.value('modlistSortOrder', 1, int))
                    else Qt.AscendingOrder, False)
            except Exception as e:
                logger.exception(f'could not restore sort order: {e}')
        self.horizontalHeader().sortIndicatorChanged.connect(self.sortByColumn)

        self.doubleClicked.connect(self.doubleClickEvent)
        model.updateCallbacks.append(self.modelUpdateEvent)

        # setup viewport caching to counter slow resizing with many table elements
        self.resizeTimer = QTimer(self)
        self.resizeTimer.setSingleShot(True)
        self.resizeTimer.setInterval(250)
        self.resizeTimer.timeout.connect(lambda: [
            self.resizeTimer.stop(),
            self.viewport().repaint(),
        ])
        self.viewportCache = QPixmap()
        self.viewportCacheSize = QSize(0, 0)

        # TODO: enhancement: offer option to read readme and other additional text files

    def setSectionSize(self, compact: bool) -> None:
        if compact:
            self.verticalHeader().setDefaultSectionSize(25)
        else:
            self.verticalHeader().setDefaultSectionSize(30)

    @debounce(200)
    async def headerChangedEvent(self) -> None:
        settings = QSettings()
        state = self.horizontalHeader().saveState()
        # call later to work around pyqt5 StopIteration exception
        asyncio.get_running_loop().call_later(
            25 / 1000.0,
            lambda: settings.setValue('modlistHorizontalHeaderState', state))

    def modelUpdateEvent(self, model: Model) -> None:
        if not self.modCountLastUpdate and len(self.modmodel):
            # if list was empty before, auto resize columns
            self.resizeColumnsToContents()
        self.modCountLastUpdate = len(self.modmodel)

    def mouseMoveEvent(self, event: QMouseEvent) -> None:
        self.hoverIndexRow = self.indexAt(event.pos()).row()
        return super().mouseMoveEvent(event)

    def wheelEvent(self, event: QWheelEvent) -> None:
        result = super().wheelEvent(event)
        # repaint previously hovered row on scroll avoid repaint artifacts
        index = self.model().index(self.hoverIndexRow, 0)
        self.hoverIndexRow = self.indexAt(event.position().toPoint()).row()
        rect = self.visualRect(index)
        rect.setLeft(0)
        rect.setRight(self.viewport().width())
        self.viewport().repaint(rect)
        return result

    def leaveEvent(self, event: QEvent) -> None:
        index = self.model().index(self.hoverIndexRow, 0)
        # unset row hover state and repaint previously hovered row
        self.hoverIndexRow = -1
        rect = self.visualRect(index)
        rect.setLeft(0)
        rect.setRight(self.viewport().width())
        self.viewport().repaint(rect)
        return super().leaveEvent(event)

    def doubleClickEvent(self, index: QModelIndex) -> None:
        if self.filtermodel.mapToSource(index).column() == 0:
            mod = self.modmodel[self.filtermodel.mapToSource(index).row()]
            if mod.enabled:
                asyncio.create_task(self.modmodel.disable(mod))
            else:
                asyncio.create_task(self.modmodel.enable(mod))

    def resizeEvent(self, event: QResizeEvent) -> None:
        super().resizeEvent(event)
        if not self.resizeTimer.isActive(
        ) and event.size() != self.viewportCacheSize:
            self.viewportCacheSize = event.size()
            self.viewportCache = self.viewport().grab()
            self.resizeTimer.start()

    def paintEvent(self, event: QPaintEvent) -> None:
        if self.resizeTimer.isActive():
            painter = QPainter(self.viewport())
            painter.drawPixmap(0, 0, self.viewportCache)
        else:
            super().paintEvent(event)

    def selectionChanged(self, selected: QItemSelection,
                         deselected: QItemSelection) -> None:
        super().selectionChanged(selected, deselected)

    def eventFilter(self, obj: QObject, event: QEvent) -> bool:
        return super().eventFilter(obj, event)

    def sortByColumn(self,
                     col: int,
                     order: Qt.SortOrder,
                     save: bool = True) -> None:  # type: ignore
        if save and col is not None and order is not None:
            settings = QSettings()
            settings.setValue('modlistSortColumn', col)
            settings.setValue('modlistSortOrder',
                              0 if order == Qt.AscendingOrder else 1)
        super().sortByColumn(col, order)

    def showContextMenu(self, pos: QPoint) -> None:
        mods = self.getSelectedMods()
        if not mods:
            return
        menu = QMenu(self)
        actionOpen = menu.addAction('&Open Directory')
        actionOpen.setIcon(
            QIcon(str(getRuntimePath('resources/icons/open-folder.ico'))))
        actionOpen.triggered.connect(lambda: [
            util.openDirectory(self.modmodel.getModPath(mod))  # type: ignore
            for mod in mods
        ])
        menu.addSeparator()
        actionEnable = menu.addAction('&Enable')
        actionEnable.triggered.connect(
            lambda: [asyncio.create_task(self.enableSelectedMods(True))])
        actionEnable.setEnabled(not all(mod.enabled for mod in mods))
        actionDisable = menu.addAction('&Disable')
        actionDisable.triggered.connect(
            lambda: [asyncio.create_task(self.enableSelectedMods(False))])
        actionDisable.setEnabled(not all(not mod.enabled for mod in mods))
        menu.addSeparator()
        actionUninstall = menu.addAction('&Uninstall')
        actionUninstall.triggered.connect(
            lambda: [asyncio.create_task(self.deleteSelectedMods())])
        menu.addSeparator()
        actionOpenNexus = menu.addAction('Open &Nexus Mods page')
        actionOpenNexus.setIcon(
            QIcon(str(getRuntimePath('resources/icons/browse.ico'))))
        actionOpenNexus.triggered.connect(lambda: [
            QDesktopServices.openUrl(
                QUrl(f'https://www.nexusmods.com/witcher3/mods/{modid}'))
            for modid in {mod.modid
                          for mod in mods if mod.modid > 0}
        ])
        actionOpenNexus.setEnabled(not all(mod.modid <= 0 for mod in mods))

        menu.popup(self.viewport().mapToGlobal(pos))

    def selectRowChecked(self, row: int) -> None:
        nums: int = self.filtermodel.rowCount()
        if row < nums and row >= 0:
            self.selectRow(row)
        elif nums > 0:
            self.selectRow(nums - 1)

    def getSelectedMods(self) -> List[Mod]:
        return [
            self.modmodel[self.filtermodel.mapToSource(index).row()]
            for index in self.selectionModel().selectedRows()
        ]

    async def enableSelectedMods(self, enable: bool = True) -> None:
        if not self.selectionModel().hasSelection():
            return
        mods = self.getSelectedMods()
        self.setDisabled(True)
        for mod in mods:
            try:
                if enable:
                    await self.modmodel.enable(mod)
                else:
                    await self.modmodel.disable(mod)
            except Exception as e:
                logger.bind(name=mod.filename).exception(
                    f'Could not enable/disable mod: {e}')
        self.setDisabled(False)
        self.setFocus()

    async def deleteSelectedMods(self) -> None:
        if not self.selectionModel().hasSelection():
            return
        self.setDisabled(True)
        mods = self.getSelectedMods()
        # TODO: incomplete: ask if selected mods should really be removed
        inds = self.selectedIndexes()
        self.selectionModel().clear()
        for mod in mods:
            try:
                await self.modmodel.remove(mod)
            except Exception as e:
                logger.bind(
                    name=mod.filename).exception(f'Could not delete mod: {e}')
        asyncio.get_running_loop().call_later(
            100 / 1000.0, partial(self.selectRowChecked, inds[0].row()))
        self.setDisabled(False)
        self.setFocus()

    async def updateModDetails(self, mod: Mod) -> bool:
        logger.bind(name=mod.filename,
                    dots=True).debug('Requesting details for mod')
        if not mod.md5hash:
            logger.bind(name=mod.filename).warning(
                'Could not get details for mod not installed from archive')
            return False
        try:
            details = await getModInformation(mod.md5hash)
        except Exception as e:
            logger.bind(name=mod.filename).warning(f'{e}')
            return False
        try:
            package = str(details[0]['mod']['name'])
            summary = str(details[0]['mod']['summary'])
            modid = int(details[0]['mod']['mod_id'])
            category = int(details[0]['mod']['category_id'])
            version = str(details[0]['file_details']['version'])
            fileid = int(details[0]['file_details']['file_id'])
            uploadname = str(details[0]['file_details']['name'])
            uploadtime = str(details[0]['file_details']['uploaded_time'])
            mod.package = package
            mod.summary = summary
            mod.modid = modid
            mod.category = getCategoryName(category)
            mod.version = version
            mod.fileid = fileid
            mod.uploadname = uploadname
            uploaddate = dateparser.parse(uploadtime)
            if uploaddate:
                mod.uploaddate = uploaddate.astimezone(tz=timezone.utc)
            else:
                logger.bind(name=mod.filename).debug(
                    f'Could not parse date {uploadtime} in mod information response'
                )
        except KeyError as e:
            logger.bind(name=mod.filename).exception(
                f'Could not find key "{str(e)}" in mod information response')
            return False
        try:
            await self.modmodel.update(mod)
        except Exception as e:
            logger.bind(
                name=mod.filename).exception(f'Could not update mod: {e}')
            return False
        return True

    async def updateSelectedModsDetails(self) -> None:
        if not self.selectionModel().hasSelection():
            return
        self.setDisabled(True)
        updatetime = datetime.now(tz=timezone.utc)
        mods = self.getSelectedMods()
        logger.bind(
            newline=True,
            output=False).debug(f'Requesting details for {len(mods)} mods')
        results = await asyncio.gather(
            *[self.updateModDetails(mod) for mod in mods],
            loop=asyncio.get_running_loop(),
            return_exceptions=True)
        successes = sum(results)
        errors = len(results) - successes
        message = 'Updated details for {0} mods{1}'.format(
            successes, f' ({errors} errors)' if errors else '')
        if errors:
            logger.warning(message)
        else:
            logger.success(message)
        self.modmodel.setLastUpdateTime(updatetime)
        self.setDisabled(False)
        self.setFocus()

    async def changeSelectedModsPriority(self, delta: int) -> None:
        mods = self.getSelectedMods()
        await asyncio.gather(*[
            self.modmodel.setPriority(
                mod, max(-1, min(9999, int(mod.priority + delta))))
            for mod in mods if mod.datatype in (
                'mod',
                'udf',
            )
        ],
                             loop=asyncio.get_running_loop())
        self.modmodel.setLastUpdateTime(datetime.now(tz=timezone.utc))

    def keyPressEvent(self, event: QKeyEvent) -> None:
        if event.key() == Qt.Key_Escape:
            self.selectionModel().clear()
        elif event.matches(QKeySequence.Delete):
            asyncio.create_task(self.deleteSelectedMods())
        elif event.modifiers(
        ) & Qt.ControlModifier == Qt.ControlModifier and event.key(
        ) == Qt.Key_Up:
            asyncio.create_task(self.changeSelectedModsPriority(1))
        elif event.modifiers(
        ) & Qt.ControlModifier == Qt.ControlModifier and event.key(
        ) == Qt.Key_Down:
            asyncio.create_task(self.changeSelectedModsPriority(-1))
        elif event.modifiers(
        ) & Qt.ControlModifier == Qt.ControlModifier and event.key(
        ) == Qt.Key_P:
            index = self.selectionModel().selectedRows()[0]
            index = index.sibling(index.row(), 5)
            if index.flags() & Qt.ItemIsEditable:
                self.setCurrentIndex(index)
                self.edit(index)
        else:
            super().keyPressEvent(event)

    def setFilter(self, search: str) -> None:
        self.filtermodel.setFilterRegularExpression(
            QRegularExpression(search,
                               QRegularExpression.CaseInsensitiveOption))

    async def checkInstallFromURLs(self,
                                   paths: List[Union[str, QUrl]],
                                   local: bool = True,
                                   web: bool = True) -> None:
        await self.installLock.acquire()
        installed = 0
        errors = 0
        installtime = datetime.now(tz=timezone.utc)
        # remove duplicate paths
        paths = list(set(paths))
        logger.bind(newline=True,
                    output=False).debug('Starting install from URLs')
        try:
            results = await asyncio.gather(*[
                self.installFromURL(path, local, web, installtime)
                for path in paths
            ],
                                           loop=asyncio.get_running_loop())
            for result in results:
                installed += result[0]
                errors += result[1]
        except Exception as e:
            # we should never land here, but don't lock up the UI if it happens
            logger.exception(str(e))
            errors += 1

        if installed > 0 or errors > 0:
            log = logger.bind(modlist=bool(installed))
            message = 'Installed {0} mods{1}'.format(
                installed, f' ({errors} errors)' if errors else '')
            if installed > 0 and errors > 0:
                log.warning(message)
            elif installed > 0:
                log.success(message)
            else:
                log.error(message)
        self.setDisabled(False)
        self.setFocus()
        self.installLock.release()

    async def installFromURL(
            self,
            path: Union[str, QUrl],
            local: bool = True,
            web: bool = True,
            installtime: Optional[datetime] = None) -> Tuple[int, int]:
        installed = 0
        errors = 0
        if not installtime:
            installtime = datetime.now(tz=timezone.utc)
        if isinstance(path, QUrl):
            path = path.toString()
        if web and isValidModDownloadUrl(path):
            self.setDisabled(True)
            logger.bind(dots=True, path=path).info(f'Installing mods from')
            i, e = await self.installFromFileDownload(path, installtime)
            installed += i
            errors += e
        elif local and isValidFileUrl(path):
            self.setDisabled(True)
            path = QUrl(path)
            logger.bind(dots=True, path=Path(
                path.toLocalFile())).info(f'Installing mods from')
            i, e = await self.installFromFile(Path(path.toLocalFile()),
                                              installtime)
            installed += i
            errors += e
        else:
            logger.bind(path=path).error('Could not install mods from')
        return installed, errors

    async def installFromFileDownload(
            self,
            url: str,
            installtime: Optional[datetime] = None) -> Tuple[int, int]:
        installed = 0
        errors = 0
        if not installtime:
            installtime = datetime.now(tz=timezone.utc)
        try:
            target = Path(urlparse(url).path)
            filename = re.sub(r'[^\w\-_\. ]', '_', unquote(target.name))
            target = Path(tempfile.gettempdir()).joinpath(
                'w3modmanager/download').joinpath(f'{filename}')
        except ValueError:
            logger.bind(name=url).exception('Wrong request URL')
            return 0, 1
        try:
            target.parent.mkdir(parents=True, exist_ok=True)
            logger.bind(name=url).info('Starting to download file')
            await downloadFile(url, target)
            installed, errors = await self.installFromFile(target, installtime)
        except (RequestError, ResponseError, Exception) as e:
            logger.bind(name=url).exception(f'Failed to download file: {e}')
            return 0, 1
        except Exception as e:
            logger.exception(str(e))
            return 0, 1
        finally:
            if target.is_file():
                target.unlink()
        return installed, errors

    async def installFromFile(
            self,
            path: Path,
            installtime: Optional[datetime] = None) -> Tuple[int, int]:
        originalpath = path
        installed = 0
        errors = 0
        archive = path.is_file()
        source = None
        md5hash = ''
        details = None
        detailsrequest: Optional[asyncio.Task] = None

        if not installtime:
            installtime = datetime.now(tz=timezone.utc)
        try:
            if archive:
                # unpack archive, set source and request details
                md5hash = getMD5Hash(path)
                source = path
                settings = QSettings()
                if settings.value('nexusGetInfo', 'False') == 'True':
                    logger.bind(
                        path=str(path),
                        dots=True).debug('Requesting details for archive')
                    detailsrequest = asyncio.create_task(
                        getModInformation(md5hash))
                logger.bind(path=str(path),
                            dots=True).debug('Unpacking archive')
                path = await extractMod(source)

            # validate and read mod
            valid, exhausted = containsValidMod(path, searchlimit=8)
            if not valid:
                if not exhausted and self.showContinueSearchDialog(
                        searchlimit=8):
                    if not containsValidMod(path):
                        raise InvalidPathError(path, 'Invalid mod')
                elif not exhausted:
                    raise InvalidPathError(path, 'Stopped searching for mod')
                else:
                    raise InvalidPathError(path, 'Invalid mod')
            mods = await Mod.fromDirectory(path, searchCommonRoot=not archive)

            installedMods = []
            # update mod details and add mods to the model
            for mod in mods:
                mod.md5hash = md5hash
                try:
                    # TODO: incomplete: check if mod is installed, ask if replace
                    await self.modmodel.add(mod)
                    installedMods.append(mod)
                    installed += 1
                except ModExistsError:
                    logger.bind(path=source if source else mod.source,
                                name=mod.filename).error(f'Mod exists')
                    errors += 1
                    continue

            # wait for details response if requested
            if detailsrequest:
                try:
                    details = await detailsrequest
                except (RequestError, ResponseError, Exception) as e:
                    logger.warning(
                        f'Could not get information for {source.name if source else path.name}: {e}'
                    )

            # update mod with additional information
            if source or details:
                for mod in installedMods:
                    if source:
                        # set source if it differs from the scan directory, e.g. an archive
                        mod.source = source
                    if details:
                        # set additional details if requested and available
                        try:
                            package = str(details[0]['mod']['name'])
                            summary = str(details[0]['mod']['summary'])
                            modid = int(details[0]['mod']['mod_id'])
                            category = int(details[0]['mod']['category_id'])
                            version = str(
                                details[0]['file_details']['version'])
                            fileid = int(details[0]['file_details']['file_id'])
                            uploadname = str(
                                details[0]['file_details']['name'])
                            uploadtime = str(
                                details[0]['file_details']['uploaded_time'])
                            mod.package = package
                            mod.summary = summary
                            mod.modid = modid
                            mod.category = getCategoryName(category)
                            mod.version = version
                            mod.fileid = fileid
                            mod.uploadname = uploadname
                            uploaddate = dateparser.parse(uploadtime)
                            if uploaddate:
                                mod.uploaddate = uploaddate.astimezone(
                                    tz=timezone.utc)
                            else:
                                logger.bind(name=mod.filename).debug(
                                    f'Could not parse date {uploadtime} in mod information response'
                                )
                        except KeyError as e:
                            logger.bind(name=mod.filename).exception(
                                f'Could not find key "{str(e)}" in mod information response'
                            )
                    try:
                        await self.modmodel.update(mod)
                    except Exception:
                        logger.bind(name=mod.filename).warning(
                            'Could not update mod details')

        except ModelError as e:
            logger.bind(path=e.path).error(e.message)
            errors += 1
        except InvalidPathError as e:
            # TODO: enhancement: better install error message
            logger.bind(path=e.path).error(e.message)
            errors += 1
        except FileNotFoundError as e:
            logger.bind(
                path=e.filename).error(e.strerror if e.strerror else str(e))
            errors += 1
        except OSError as e:
            logger.bind(
                path=e.filename).error(e.strerror if e.strerror else str(e))
            errors += 1
        except Exception as e:
            logger.exception(str(e))
            errors += 1
        finally:
            if detailsrequest and not detailsrequest.done():
                detailsrequest.cancel()
            if archive and not path == originalpath:
                try:
                    util.removeDirectory(path)
                except Exception:
                    logger.bind(path=path).warning(
                        'Could not remove temporary directory')
            self.modmodel.setLastUpdateTime(installtime)
            self.repaint()
        return installed, errors

    def showContinueSearchDialog(self, searchlimit: int) -> bool:
        messagebox = QMessageBox(self)
        messagebox.setWindowTitle('Unusual search depth')
        messagebox.setText(f'''
            <p>No mod detected after searching through {searchlimit} directories.</p>
            <p>Are you sure this is a valid mod?</p>
            ''')
        messagebox.setTextFormat(Qt.RichText)
        messagebox.setStandardButtons(QMessageBox.Cancel)
        yes: QPushButton = QPushButton(' Yes, continue searching ', messagebox)
        yes.setAutoDefault(True)
        yes.setDefault(True)
        messagebox.addButton(yes, QMessageBox.YesRole)
        messagebox.exec_()
        return messagebox.clickedButton() == yes

    def dropEvent(self, event: QDropEvent) -> None:
        event.accept()
        self.setDisabled(True)
        self.repaint()
        asyncio.create_task(self.checkInstallFromURLs(event.mimeData().urls()))

    def dragEnterEvent(self, event: QDragEnterEvent) -> None:
        self.setDisabled(True)
        self.repaint()
        urls = event.mimeData().urls()
        if not urls:
            self.setDisabled(False)
            self.setFocus()
            event.ignore()
            return
        for url in urls:
            try:
                parse = urlparse(url.toString())
                if parse.scheme not in ['file']:
                    self.setDisabled(False)
                    event.ignore()
                    return
                filepath = Path(url.toLocalFile())
                if isArchive(filepath) or containsValidMod(filepath,
                                                           searchlimit=8)[0]:
                    self.setDisabled(False)
                    event.accept()
                    return
            except Exception as e:
                logger.debug(str(e))
        self.setDisabled(False)
        self.setFocus()
        event.ignore()

    def dragMoveEvent(self, event: QDragMoveEvent) -> None:
        event.accept()

    def dragLeaveEvent(self, event: QDragLeaveEvent) -> None:
        event.accept()
Beispiel #19
0
class ByteView(QAbstractScrollArea, View):
    def __init__(self, parent, data):
        QAbstractScrollArea.__init__(self, parent)
        View.__init__(self)
        View.setBinaryDataNavigable(self, True)
        self.setupView(self)
        self.data = data
        self.byte_mapping = [
            u' ', u'☺', u'☻', u'♥', u'♦', u'♣', u'♠', u'•', u'◘', u'○', u'◙',
            u'♂', u'♀', u'♪', u'♫', u'☼', u'▸', u'◂', u'↕', u'‼', u'¶', u'§',
            u'▬', u'↨', u'↑', u'↓', u'→', u'←', u'∟', u'↔', u'▴', u'▾', u' ',
            u'!', u'"', u'#', u'$', u'%', u'&', u'\'', u'(', u')', u'*', u'+',
            u',', u'-', u'.', u'/', u'0', u'1', u'2', u'3', u'4', u'5', u'6',
            u'7', u'8', u'9', u':', u';', u'<', u'=', u'>', u'?', u'@', u'A',
            u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', u'K', u'L',
            u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', u'U', u'V', u'W',
            u'X', u'Y', u'Z', u'[', u'\\', u']', u'^', u'_', u'`', u'a', u'b',
            u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', u'k', u'l', u'm',
            u'n', u'o', u'p', u'q', u'r', u's', u't', u'u', u'v', u'w', u'x',
            u'y', u'z', u'{', u'|', u'}', u'~', u'⌂', u'Ç', u'ü', u'é', u'â',
            u'ä', u'à', u'å', u'ç', u'ê', u'ë', u'è', u'ï', u'î', u'ì', u'Ä',
            u'Å', u'É', u'æ', u'Æ', u'ô', u'ö', u'ò', u'û', u'ù', u'ÿ', u'Ö',
            u'Ü', u'¢', u'£', u'¥', u'₧', u'ƒ', u'á', u'í', u'ó', u'ú', u'ñ',
            u'Ñ', u'ª', u'º', u'¿', u'⌐', u'¬', u'½', u'¼', u'¡', u'«', u'»',
            u'░', u'▒', u'▓', u'│', u'┤', u'╡', u'╢', u'╖', u'╕', u'╣', u'║',
            u'╗', u'╝', u'╜', u'╛', u'┐', u'└', u'┴', u'┬', u'├', u'─', u'┼',
            u'╞', u'╟', u'╚', u'╔', u'╩', u'╦', u'╠', u'═', u'╬', u'╧', u'╨',
            u'╤', u'╥', u'╙', u'╘', u'╒', u'╓', u'╫', u'╪', u'┘', u'┌', u'█',
            u'▄', u'▌', u'▐', u'▀', u'α', u'ß', u'Γ', u'π', u'Σ', u'σ', u'µ',
            u'τ', u'Φ', u'Θ', u'Ω', u'δ', u'∞', u'φ', u'ε', u'∩', u'≡', u'±',
            u'≥', u'≤', u'⌠', u'⌡', u'÷', u'≈', u'°', u'∙', u'·', u'√', u'ⁿ',
            u'²', u'■', u' '
        ]

        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.setFocusPolicy(Qt.StrongFocus)

        self.cursorAddr = self.data.start
        self.prevCursorAddr = self.cursorAddr
        self.selectionStartAddr = self.cursorAddr
        self.topAddr = self.cursorAddr
        self.topLine = 0
        self.selectionVisible = False
        self.caretVisible = False
        self.caretBlink = True
        self.leftButtonDown = False
        self.cols = 128
        self.updatesRequired = False
        self.visibleRows = 1
        self.lines = []

        self.updateRanges()

        areaSize = self.viewport().size()
        self.adjustSize(areaSize.width(), areaSize.height())

        if self.allocatedLength > 0x7fffffff:
            self.scrollBarMultiplier = (self.allocatedLength // 0x7fffffff) + 1
        else:
            self.scrollBarMultiplier = 1
        self.wheelDelta = 0
        self.updatingScrollBar = False
        self.verticalScrollBar().setRange(0, (self.allocatedLength - 1) //
                                          self.scrollBarMultiplier)
        self.verticalScrollBar().sliderMoved.connect(self.scrollBarMoved)
        self.verticalScrollBar().actionTriggered.connect(self.scrollBarAction)

        self.cursorTimer = QTimer(self)
        self.cursorTimer.setInterval(500)
        self.cursorTimer.setSingleShot(False)
        self.cursorTimer.timeout.connect(self.cursorTimerEvent)
        self.cursorTimer.start()

        self.updateTimer = QTimer(self)
        self.updateTimer.setInterval(200)
        self.updateTimer.setSingleShot(False)
        #self.updateTimer.timeout.connect(self.updateTimerEvent)

        self.actionHandler().bindAction("Move Cursor Up",
                                        UIAction(lambda ctxt: self.up(False)))
        self.actionHandler().bindAction(
            "Move Cursor Down", UIAction(lambda ctxt: self.down(False)))
        self.actionHandler().bindAction(
            "Move Cursor Left", UIAction(lambda ctxt: self.left(1, False)))
        self.actionHandler().bindAction(
            "Move Cursor Right", UIAction(lambda ctxt: self.right(1, False)))
        self.actionHandler().bindAction(
            "Move Cursor Word Left",
            UIAction(lambda ctxt: self.left(8, False)))
        self.actionHandler().bindAction(
            "Move Cursor Word Right",
            UIAction(lambda ctxt: self.right(8, False)))
        self.actionHandler().bindAction("Extend Selection Up",
                                        UIAction(lambda ctxt: self.up(True)))
        self.actionHandler().bindAction("Extend Selection Down",
                                        UIAction(lambda ctxt: self.down(True)))
        self.actionHandler().bindAction(
            "Extend Selection Left", UIAction(lambda ctxt: self.left(1, True)))
        self.actionHandler().bindAction(
            "Extend Selection Right",
            UIAction(lambda ctxt: self.right(1, True)))
        self.actionHandler().bindAction(
            "Extend Selection Word Left",
            UIAction(lambda ctxt: self.left(8, True)))
        self.actionHandler().bindAction(
            "Extend Selection Word Right",
            UIAction(lambda ctxt: self.right(8, True)))
        self.actionHandler().bindAction(
            "Page Up", UIAction(lambda ctxt: self.pageUp(False)))
        self.actionHandler().bindAction(
            "Page Down", UIAction(lambda ctxt: self.pageDown(False)))
        self.actionHandler().bindAction(
            "Extend Selection Page Up",
            UIAction(lambda ctxt: self.pageUp(True)))
        self.actionHandler().bindAction(
            "Extend Selection Page Down",
            UIAction(lambda ctxt: self.pageDown(True)))
        self.actionHandler().bindAction(
            "Move Cursor to Start of Line",
            UIAction(lambda ctxt: self.moveToStartOfLine(False)))
        self.actionHandler().bindAction(
            "Move Cursor to End of Line",
            UIAction(lambda ctxt: self.moveToEndOfLine(False)))
        self.actionHandler().bindAction(
            "Move Cursor to Start of View",
            UIAction(lambda ctxt: self.moveToStartOfView(False)))
        self.actionHandler().bindAction(
            "Move Cursor to End of View",
            UIAction(lambda ctxt: self.moveToEndOfView(False)))
        self.actionHandler().bindAction(
            "Extend Selection to Start of Line",
            UIAction(lambda ctxt: self.moveToStartOfLine(True)))
        self.actionHandler().bindAction(
            "Extend Selection to End of Line",
            UIAction(lambda ctxt: self.moveToEndOfLine(True)))
        self.actionHandler().bindAction(
            "Extend Selection to Start of View",
            UIAction(lambda ctxt: self.moveToStartOfView(True)))
        self.actionHandler().bindAction(
            "Extend Selection to End of View",
            UIAction(lambda ctxt: self.moveToEndOfView(True)))

    def getData(self):
        return self.data

    def getStart(self):
        return self.data.start

    def getEnd(self):
        return self.data.end

    def getLength(self):
        return self.getEnd() - self.getStart()

    def getCurrentOffset(self):
        return self.cursorAddr

    def getSelectionOffsets(self):
        start = self.selectionStartAddr
        end = self.cursorAddr
        if end < start:
            t = start
            start = end
            end = t
        return (start, end)

    def updateRanges(self):
        self.ranges = self.data.allocated_ranges
        # Remove regions not backed by the file
        for i in self.data.segments:
            if i.data_length < len(i):
                self.removeRange(i.start + i.data_length, i.end)
        self.allocatedLength = 0
        for i in self.ranges:
            self.allocatedLength += i.end - i.start

    def removeRange(self, begin, end):
        newRanges = []
        for i in self.ranges:
            if (end <= i.start) or (begin >= i.end):
                newRanges.append(i)
            elif (begin <= i.start) and (end >= i.end):
                continue
            elif (begin <= i.start) and (end < i.end):
                newRanges.append(AddressRange(end, i.end))
            elif (begin > i.start) and (end >= i.end):
                newRanges.append(AddressRange(i.start, begin))
            else:
                newRanges.append(AddressRange(i.start, begin))
                newRanges.append(AddressRange(end, i.end))
        self.ranges = newRanges

    def setTopToAddress(self, addr):
        for i in self.ranges:
            if (addr >= i.start) and (addr <= i.end):
                self.topAddr = addr - ((addr - i.start) % self.cols)
                if self.topAddr < i.start:
                    self.topAddr = i.start
                return
            if i.start > addr:
                self.topAddr = i.start
                return
        self.topAddr = self.data.end

    def navigate(self, addr):
        if addr < self.getStart():
            return False
        if addr > self.getEnd():
            return False
        self.cursorAddr = self.getStart()
        for i in self.ranges:
            if i.start > addr:
                break
            if addr > i.end:
                self.cursorAddr = i.end
            elif addr >= i.start:
                self.cursorAddr = addr
            else:
                self.cursorAddr = i.start
        self.setTopToAddress(self.cursorAddr)
        self.refreshLines()
        self.showContextAroundTop()
        self.selectNone()
        self.repositionCaret()
        return True

    def updateFonts(self):
        areaSize = self.viewport().size()
        self.adjustSize(areaSize.width(), areaSize.height())

    def getFont(self):
        userFont = binaryninjaui.getMonospaceFont(self)
        if sys.platform == "darwin":
            # Some fonts aren't fixed width across all characters, use a known good one
            font = QFont("Menlo", userFont.pointSize())
            font.setKerning(False)
        else:
            font = userFont
        return font

    def createRenderContext(self):
        render = RenderContext(self)
        render.setFont(self.getFont())
        return render

    def adjustSize(self, width, height):
        self.addrWidth = max(len("%x" % self.data.end), 8)
        render = self.createRenderContext()
        cols = ((width - 4) // render.getFontWidth()) - (self.addrWidth + 2)
        if cols < 1:
            cols = 1
        if cols != self.cols:
            self.cols = cols
            if self.topLine < len(self.lines):
                self.setTopToAddress(self.lines[self.topLine].address)
            else:
                self.setTopToAddress(self.cursorAddr)
            self.refreshLines()
        self.visibleRows = (height - 4) // render.getFontHeight()
        self.verticalScrollBar().setPageStep(self.visibleRows * self.cols //
                                             self.scrollBarMultiplier)
        self.refreshAtCurrentLocation()
        self.viewport().update()

    def getContiguousOffsetForAddress(self, addr):
        offset = 0
        for i in self.ranges:
            if (addr >= i.start) and (addr <= i.end):
                offset += addr - i.start
                break
            offset += i.end - i.start
        return offset

    def getAddressForContiguousOffset(self, offset):
        cur = 0
        for i in self.ranges:
            if offset < (cur + (i.end - i.start)):
                return i.start + (offset - cur)
            cur += i.end - i.start
        return self.data.end

    def refreshLines(self):
        addr = self.topAddr
        self.lines = []
        self.topLine = 0
        self.bottomAddr = self.topAddr
        self.updateRanges()
        if self.allocatedLength > 0x7fffffff:
            self.scrollBarMultiplier = (self.allocatedLength // 0x7fffffff) + 1
        else:
            self.scrollBarMultiplier = 1
        self.updatingScrollBar = True
        self.verticalScrollBar().setRange(0, (self.allocatedLength - 1) //
                                          self.scrollBarMultiplier)
        self.verticalScrollBar().setValue(
            self.getContiguousOffsetForAddress(addr) //
            self.scrollBarMultiplier)
        self.updatingScrollBar = False
        self.updateCache()
        self.viewport().update()
        UIContext.updateStatus()

    def refreshAtCurrentLocation(self):
        if self.topLine < len(self.lines):
            self.topAddr = self.lines[self.topLine].address
        self.refreshLines()

    def createLine(self, addr, length, separator):
        if separator:
            return ByteViewLine(addr, length, u'', True)
        else:
            data = self.data.read(addr, length)
            text = u''.join([self.byte_mapping[value] for value in data])
            return ByteViewLine(addr, length, text, False)

    def cachePreviousLines(self):
        prevEnd = None
        for i in self.ranges:
            if (self.topAddr > i.start) and (self.topAddr <= i.end):
                startLine = self.topAddr - (
                    (self.topAddr - i.start) % self.cols)
                if startLine == self.topAddr:
                    startLine -= self.cols
                if startLine < i.start:
                    startLine = i.start
                line = self.createLine(startLine, self.topAddr - startLine,
                                       False)
                self.lines.insert(0, line)
                self.topLine += 1
                self.topAddr = startLine
                return True
            elif i.start >= self.topAddr:
                if prevEnd is None:
                    return False
                line = self.createLine(prevEnd, i.start - prevEnd, True)
                self.lines.insert(0, line)
                self.topLine += 1
                self.topAddr = prevEnd
                return True
            prevEnd = i.end
        if prevEnd is None:
            return False
        line = self.createLine(prevEnd, self.topAddr - prevEnd, True)
        self.lines.insert(0, line)
        self.topLine += 1
        self.topAddr = prevEnd
        return True

    def cacheNextLines(self):
        lastAddr = self.data.start
        for i in self.ranges:
            if (self.bottomAddr >= i.start) and (self.bottomAddr < i.end):
                endLine = self.bottomAddr + self.cols
                if endLine > i.end:
                    endLine = i.end
                line = self.createLine(self.bottomAddr,
                                       endLine - self.bottomAddr, False)
                self.lines.append(line)
                self.bottomAddr = endLine
                return True
            elif i.start > self.bottomAddr:
                line = self.createLine(self.bottomAddr,
                                       i.start - self.bottomAddr, True)
                self.lines.append(line)
                self.bottomAddr = i.start
                return True
            lastAddr = i.end
        if self.bottomAddr == lastAddr:
            # Ensure there is a place for the cursor at the end of the file
            if (len(self.lines) > 0) and (self.lines[-1].length != self.cols):
                return False
            line = self.createLine(lastAddr, 0, False)
            self.lines.append(line)
            self.bottomAddr += 1
            return True
        return False

    def updateCache(self):
        # Cache enough for the current page and the next page
        while (len(self.lines) - self.topLine) <= (self.visibleRows * 2):
            if not self.cacheNextLines():
                break
        # Cache enough for the previous page
        while self.topLine <= self.visibleRows:
            if not self.cachePreviousLines():
                break
        # Trim cache
        if self.topLine > (self.visibleRows * 4):
            self.lines = self.lines[self.topLine - (self.visibleRows * 4):]
            self.topLine = self.visibleRows * 4
            self.topAddr = self.lines[0].address
        if (len(self.lines) - self.topLine) > (self.visibleRows * 5):
            self.bottomAddr = self.lines[self.topLine +
                                         (self.visibleRows * 5)].address
            self.lines = self.lines[0:self.topLine + (self.visibleRows * 5)]

    def scrollLines(self, count):
        newOffset = self.topLine + count
        if newOffset < 0:
            self.topLine = 0
        elif newOffset >= len(self.lines):
            self.topLine = len(self.lines) - 1
        else:
            self.topLine = newOffset
        self.updateCache()
        self.viewport().update()
        if self.topLine < len(self.lines):
            self.updatingScrollBar = True
            addr = self.lines[self.topLine].address
            self.verticalScrollBar().setValue(
                self.getContiguousOffsetForAddress(addr) //
                self.scrollBarMultiplier)
            self.updatingScrollBar = False

    def showContextAroundTop(self):
        scroll = self.visibleRows // 4
        if scroll > self.topLine:
            self.topLine = 0
        else:
            self.topLine -= scroll
        if self.topLine < len(self.lines):
            self.updatingScrollBar = True
            addr = self.lines[self.topLine].address
            self.verticalScrollBar().setValue(
                self.getContiguousOffsetForAddress(addr) //
                self.scrollBarMultiplier)
            self.updatingScrollBar = False
        self.updateCache()

    def repositionCaret(self):
        self.updateCache()
        found = False
        for i in range(0, len(self.lines)):
            if (((self.cursorAddr >= self.lines[i].address) and
                 (self.cursorAddr <
                  (self.lines[i].address + self.lines[i].length)))
                    or (((i + 1) == len(self.lines)) and
                        (self.cursorAddr
                         == (self.lines[i].address + self.lines[i].length)))):
                if i < self.topLine:
                    self.topLine = i
                elif i > (self.topLine + self.visibleRows - 1):
                    self.topLine = i - (self.visibleRows - 1)
                self.updatingScrollBar = True
                addr = self.lines[self.topLine].address
                self.verticalScrollBar().setValue(
                    self.getContiguousOffsetForAddress(addr) //
                    self.scrollBarMultiplier)
                self.updatingScrollBar = False
                self.updateCache()
                self.viewport().update()
                found = True
                break
        if not found:
            self.setTopToAddress(self.cursorAddr)
            self.refreshLines()
            self.showContextAroundTop()
        # Force caret to be visible and repaint
        self.caretBlink = True
        self.cursorTimer.stop()
        self.cursorTimer.start()
        self.updateCaret()
        UIContext.updateStatus()

    def updateCaret(self):
        # Rerender both the old caret position and the new caret position
        render = self.createRenderContext()
        for i in range(self.topLine,
                       min(len(self.lines), self.topLine + self.visibleRows)):
            if (((self.prevCursorAddr >= self.lines[i].address) and
                 (self.prevCursorAddr <=
                  (self.lines[i].address + self.lines[i].length)))
                    or ((self.cursorAddr >= self.lines[i].address) and
                        (self.cursorAddr <=
                         (self.lines[i].address + self.lines[i].length)))):
                self.viewport().update(0, (i - self.topLine) *
                                       render.getFontHeight(),
                                       self.viewport().size().width(),
                                       render.getFontHeight() + 3)

    def resizeEvent(self, event):
        self.adjustSize(event.size().width(), event.size().height())

    def paintEvent(self, event):
        p = QPainter(self.viewport())
        render = self.createRenderContext()
        render.init(p)
        charWidth = render.getFontWidth()
        charHeight = render.getFontHeight()

        # Compute range that needs to be updated
        topY = event.rect().y()
        botY = topY + event.rect().height()
        topY = (topY - 2) // charHeight
        botY = ((botY - 2) // charHeight) + 1

        # Compute selection range
        selection = False
        selStart, selEnd = self.getSelectionOffsets()
        if selStart != selEnd:
            selection = True

        # Draw selection
        if selection:
            startY = None
            endY = None
            startX = None
            endX = None
            for i in range(0, len(self.lines)):
                if selStart >= self.lines[i].address:
                    startY = i - self.topLine
                    startX = selStart - self.lines[i].address
                    if startX > self.cols:
                        startX = self.cols
                if selEnd >= self.lines[i].address:
                    endY = i - self.topLine
                    endX = selEnd - self.lines[i].address
                    if endX > self.cols:
                        endX = self.cols

            if startY is not None and endY is not None:
                p.setPen(binaryninjaui.getThemeColor(
                    ThemeColor.SelectionColor))
                p.setBrush(
                    binaryninjaui.getThemeColor(ThemeColor.SelectionColor))
                if startY == endY:
                    p.drawRect(2 + (self.addrWidth + 2 + startX) * charWidth,
                               2 + startY * charHeight,
                               (endX - startX) * charWidth, charHeight + 1)
                else:
                    p.drawRect(2 + (self.addrWidth + 2 + startX) * charWidth,
                               2 + startY * charHeight,
                               (self.cols - startX) * charWidth,
                               charHeight + 1)
                    if endX > 0:
                        p.drawRect(2 + (self.addrWidth + 2) * charWidth,
                                   2 + endY * charHeight, endX * charWidth,
                                   charHeight + 1)
                if (endY - startY) > 1:
                    p.drawRect(2 + (self.addrWidth + 2) * charWidth,
                               2 + (startY + 1) * charHeight,
                               self.cols * charWidth,
                               ((endY - startY) - 1) * charHeight + 1)

        # Paint each line
        color = self.palette().color(QPalette.WindowText)
        for y in range(topY, botY):
            if (y + self.topLine) < 0:
                continue
            if (y + self.topLine) >= len(self.lines):
                break
            if self.lines[y + self.topLine].separator:
                render.drawLinearDisassemblyLineBackground(
                    p,
                    LinearDisassemblyLineType.NonContiguousSeparatorLineType,
                    QRect(0, 2 + y * charHeight,
                          event.rect().width(), charHeight), 0)
                continue

            lineStartAddr = self.lines[y + self.topLine].address
            addrStr = "%.8x" % lineStartAddr
            length = self.lines[y + self.topLine].length
            text = self.lines[y + self.topLine].text

            cursorCol = None
            if (((self.cursorAddr >= lineStartAddr) and
                 (self.cursorAddr < (lineStartAddr + length)))
                    or (((y + self.topLine + 1) >= len(self.lines)) and
                        (self.cursorAddr == (lineStartAddr + length)))):
                cursorCol = self.cursorAddr - lineStartAddr

            render.drawText(
                p, 2, 2 + y * charHeight,
                binaryninjaui.getThemeColor(ThemeColor.AddressColor), addrStr)
            render.drawText(p, 2 + (self.addrWidth + 2) * charWidth,
                            2 + y * charHeight, color, text)

            if self.caretVisible and self.caretBlink and not selection and cursorCol is not None:
                p.setPen(Qt.NoPen)
                p.setBrush(self.palette().color(QPalette.WindowText))
                p.drawRect(2 + (self.addrWidth + 2 + cursorCol) * charWidth,
                           2 + y * charHeight, charWidth, charHeight + 1)
                caretTextColor = self.palette().color(QPalette.Base)
                byteValue = self.data.read(lineStartAddr + cursorCol, 1)
                if len(byteValue) == 1:
                    byteStr = self.byte_mapping[byteValue[0]]
                    render.drawText(
                        p, 2 + (self.addrWidth + 2 + cursorCol) * charWidth,
                        2 + y * charHeight, caretTextColor, byteStr)

    def wheelEvent(self, event):
        if event.orientation() == Qt.Horizontal:
            return
        self.wheelDelta -= event.delta()
        if (self.wheelDelta <= -40) or (self.wheelDelta >= 40):
            lines = self.wheelDelta // 40
            self.wheelDelta -= lines * 40
            self.scrollLines(lines)

    def scrollBarMoved(self, value):
        if self.updatingScrollBar:
            return
        self.wheelDelta = 0
        addr = self.getAddressForContiguousOffset(value *
                                                  self.scrollBarMultiplier)
        self.setTopToAddress(addr)
        self.refreshLines()
        for i in range(1, len(self.lines)):
            if (self.lines[i].address + self.lines[i].length) > addr:
                self.topLine = i - 1
                break
        self.updateCache()

    def scrollBarAction(self, action):
        if action == QAbstractSlider.SliderSingleStepAdd:
            self.wheelDelta = 0
            self.scrollLines(1)
        elif action == QAbstractSlider.SliderSingleStepSub:
            self.wheelDelta = 0
            self.scrollLines(-1)
        elif action == QAbstractSlider.SliderPageStepAdd:
            self.wheelDelta = 0
            self.scrollLines(self.visibleRows)
        elif action == QAbstractSlider.SliderPageStepSub:
            self.wheelDelta = 0
            self.scrollLines(-self.visibleRows)
        elif action == QAbstractSlider.SliderToMinimum:
            self.wheelDelta = 0
            self.setTopToAddress(self.getStart())
            self.verticalScrollBar().setValue(
                self.getContiguousOffsetForAddress(self.topAddr) //
                self.scrollBarMultiplier)
            self.refreshLines()
        elif action == QAbstractSlider.SliderToMaximum:
            self.wheelDelta = 0
            self.setTopToAddress(self.getEnd())
            self.verticalScrollBar().setValue(
                self.getContiguousOffsetForAddress(self.topAddr) //
                self.scrollBarMultiplier)
            self.refreshLines()

    def cursorTimerEvent(self):
        self.caretBlink = not self.caretBlink
        self.updateCaret()

    def focusInEvent(self, event):
        self.caretVisible = True
        self.updateCaret()

    def focusOutEvent(self, event):
        self.caretVisible = False
        self.leftButtonDown = False
        self.updateCaret()

    def selectNone(self):
        for i in self.lines:
            if (self.cursorAddr >=
                    i.address) and (self.cursorAddr <
                                    (i.address + i.length)) and i.separator:
                self.cursorAddr = i.address + i.length
                break
        self.selectionStartAddr = self.cursorAddr
        if self.selectionVisible:
            self.viewport().update()
        self.repositionCaret()
        UIContext.updateStatus()

    def selectAll(self):
        self.selectionStartAddr = self.getStart()
        self.cursorAddr = self.getEnd()
        self.viewport().update()
        UIContext.updateStatus()

    def adjustAddressAfterBackwardMovement(self):
        lastAddr = self.getStart()
        for i in self.ranges:
            if (self.cursorAddr >= i.start) and (self.cursorAddr < i.end):
                break
            if i.start > self.cursorAddr:
                self.cursorAddr = lastAddr
                break
            lastAddr = i.end - 1

    def adjustAddressAfterForwardMovement(self):
        for i in self.ranges:
            if (self.cursorAddr >= i.start) and (self.cursorAddr < i.end):
                break
            if i.start > self.cursorAddr:
                self.cursorAddr = i.start
                break

    def left(self, count, selecting):
        if self.cursorAddr > (self.getStart() + count):
            self.cursorAddr -= count
        else:
            self.cursorAddr = self.getStart()
        self.adjustAddressAfterBackwardMovement()
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        if self.selectionVisible or selecting:
            self.viewport().update()

    def right(self, count, selecting):
        if self.cursorAddr <= (self.getEnd() - count):
            self.cursorAddr += count
        else:
            self.cursorAddr = self.getEnd()
        self.adjustAddressAfterForwardMovement()
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        if self.selectionVisible or selecting:
            self.viewport().update()

    def up(self, selecting):
        self.left(self.cols, selecting)

    def down(self, selecting):
        self.right(self.cols, selecting)

    def pageUp(self, selecting):
        for i in range(0, len(self.lines)):
            if (((self.cursorAddr >= self.lines[i].address) and
                 (self.cursorAddr <
                  (self.lines[i].address + self.lines[i].length)))
                    or (((i + 1) == len(self.lines)) and
                        (self.cursorAddr
                         == (self.lines[i].address + self.lines[i].length)))):
                if i < self.visibleRows:
                    self.cursorAddr = self.getStart()
                else:
                    lineOfs = self.cursorAddr - self.lines[i].address
                    self.cursorAddr = self.lines[
                        i - self.visibleRows].address + lineOfs
                    if self.cursorAddr < self.lines[i -
                                                    self.visibleRows].address:
                        self.cursorAddr = self.lines[i -
                                                     self.visibleRows].address
                    elif self.cursorAddr >= (
                            self.lines[i - self.visibleRows].address +
                            self.lines[i - self.visibleRows].length):
                        self.cursorAddr = self.lines[
                            i - self.visibleRows].address + self.lines[
                                i - self.visibleRows].length - 1
                    break
        self.adjustAddressAfterBackwardMovement()
        if self.topLine > self.visibleRows:
            self.topLine -= self.visibleRows
        else:
            self.topLine = 0
        if self.topLine < len(self.lines):
            self.updatingScrollBar = True
            addr = self.lines[self.topLine].address
            self.verticalScrollBar().setValue(
                self.getContiguousOffsetForAddress(addr) //
                self.scrollBarMultiplier)
            self.updatingScrollBar = False
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        self.viewport().update()

    def pageDown(self, selecting):
        for i in range(0, len(self.lines)):
            if (((self.cursorAddr >= self.lines[i].address) and
                 (self.cursorAddr <
                  (self.lines[i].address + self.lines[i].length)))
                    or (((i + 1) == len(self.lines)) and
                        (self.cursorAddr
                         == (self.lines[i].address + self.lines[i].length)))):
                if i >= (len(self.lines) - self.visibleRows):
                    self.cursorAddr = self.getEnd()
                else:
                    lineOfs = self.cursorAddr - self.lines[i].address
                    self.cursorAddr = self.lines[
                        i + self.visibleRows].address + lineOfs
                    if self.cursorAddr < self.lines[i +
                                                    self.visibleRows].address:
                        self.cursorAddr = self.lines[i +
                                                     self.visibleRows].address
                    elif self.cursorAddr >= (
                            self.lines[i + self.visibleRows].address +
                            self.lines[i + self.visibleRows].length):
                        self.cursorAddr = self.lines[
                            i + self.visibleRows].address + self.lines[
                                i + self.visibleRows].length - 1
                    break
        self.adjustAddressAfterForwardMovement()
        if (self.topLine + self.visibleRows) < len(self.lines):
            self.topLine += self.visibleRows
        elif len(self.lines) > 0:
            self.topLine = len(self.lines) - 1
        if self.topLine < len(self.lines):
            self.updatingScrollBar = True
            addr = self.lines[self.topLine].address
            self.verticalScrollBar().setValue(
                self.getContiguousOffsetForAddress(addr) //
                self.scrollBarMultiplier)
            self.updatingScrollBar = False
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        self.viewport().update()

    def moveToStartOfLine(self, selecting):
        for i in self.lines:
            if (self.cursorAddr >= i.address) and (self.cursorAddr <
                                                   (i.address + i.length)):
                self.cursorAddr = i.address
                break
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        if self.selectionVisible or selecting:
            self.viewport().update()

    def moveToEndOfLine(self, selecting):
        for i in self.lines:
            if (self.cursorAddr >= i.address) and (self.cursorAddr <
                                                   (i.address + i.length)):
                self.cursorAddr = i.address + i.length - 1
                break
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        if self.selectionVisible or selecting:
            self.viewport().update()

    def moveToStartOfView(self, selecting):
        self.cursorAddr = self.getStart()
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        if self.selectionVisible or selecting:
            self.viewport().update()

    def moveToEndOfView(self, selecting):
        self.cursorAddr = self.getEnd()
        if not selecting:
            self.selectNone()
        self.repositionCaret()
        if self.selectionVisible or selecting:
            self.viewport().update()

    def addressFromLocation(self, x, y):
        if y < 0:
            y = 0
        if x < 0:
            x = 0
        if x > self.cols:
            x = self.cols
        if (y + self.topLine) >= len(self.lines):
            return self.getEnd()
        if self.lines[y + self.topLine].separator:
            return self.lines[y + self.topLine].address - 1
        result = self.lines[y + self.topLine].address + x
        if result >= (self.lines[y + self.topLine].address +
                      self.lines[y + self.topLine].length):
            if (y + self.topLine) == (len(self.lines) - 1):
                return self.getEnd()
            else:
                return self.lines[y + self.topLine].address + self.lines[
                    y + self.topLine].length - 1
        return result

    def mousePressEvent(self, event):
        if event.button() != Qt.LeftButton:
            return
        render = self.createRenderContext()
        x = (event.x() - 2) // render.getFontWidth() - (self.addrWidth + 2)
        y = (event.y() - 2) // render.getFontHeight()
        self.lastMouseX = x
        self.lastMouseY = y
        self.cursorAddr = self.addressFromLocation(x, y)
        if (event.modifiers() & Qt.ShiftModifier) == 0:
            self.selectNone()
        self.repositionCaret()
        if (event.modifiers() & Qt.ShiftModifier) != 0:
            self.viewport().update()
        self.leftButtonDown = True

    def mouseMoveEvent(self, event):
        if not self.leftButtonDown:
            return
        render = self.createRenderContext()
        x = (event.x() - 2) // render.getFontWidth() - (self.addrWidth + 2)
        y = (event.y() - 2) // render.getFontHeight()
        if (x == self.lastMouseX) and (y == self.lastMouseY):
            return
        self.lastMouseX = x
        self.lastMouseY = y
        self.cursorAddr = self.addressFromLocation(x, y)
        self.repositionCaret()
        self.viewport().update()

    def mouseReleaseEvent(self, event):
        if event.button() != Qt.LeftButton:
            return
        self.leftButtonDown = False
Beispiel #20
0
class Controller:
    def __init__(self):

        # gui
        self.app = QApplication([])
        self.main_window = MainWindow(controller=self)

        # device
        self.device = Device()

        # fps stats
        self.fps_timer = QTimer()
        self.fps_timer.timeout.connect(self.update_ui_fps)
        self.spf = 1  # seconds per frame
        self.timestamp_last_capture = 0

        # acquisition thread
        self.continuous_acquisition = False
        self.worker_wait_condition = QWaitCondition()
        self.acquisition_worker = AcquisitionWorker(self.worker_wait_condition,
                                                    device=self.device)
        self.acquisition_thread = QThread()
        self.acquisition_worker.moveToThread(self.acquisition_thread)
        self.acquisition_thread.started.connect(self.acquisition_worker.run)
        self.acquisition_worker.finished.connect(self.acquisition_thread.quit)
        # self.acquisition_worker.finished.connect(self.acquisition_thread.deleteLater)
        # self.acquisition_thread.finished.connect(self.acquisition_worker.deleteLater)
        self.acquisition_worker.data_ready.connect(self.data_ready_callback)
        self.acquisition_thread.start()

        # default timebase
        self.set_timebase("20 ms")

        # on app exit
        self.app.aboutToQuit.connect(self.on_app_exit)

    def run_app(self):
        self.main_window.show()
        return self.app.exec_()

    def get_ports_names(self):
        return [p.device for p in serial.tools.list_ports.comports()]

    def update_ui_fps(self):
        fps = 1 / self.spf
        self.main_window.control_panel.stats_panel.fps_label.setText(
            f"{fps:.2f} fps")

    def set_timebase(self, timebase: str):
        # send timebase to device
        self.device.timebase = timebase
        if self.is_device_connected():
            self.device.write_timebase()
        # adjust timebase in the screen
        seconds_per_sample = (float(timebase.split()[0]) / 10 * {
            "ms": 1e-3,
            "us": 1e-6
        }[timebase.split()[1]])
        self.data_time_array = (np.arange(0, self.device.BUFFER_SIZE) *
                                seconds_per_sample)
        self.main_window.screen.setXRange(0,
                                          self.device.BUFFER_SIZE *
                                          seconds_per_sample,
                                          padding=0.02)
        self.main_window.screen.setYRange(0, 5)

    def set_trigger_state(self, on):
        self.device.trigger_on = on
        if self.is_device_connected():
            self.device.write_trigger_state()

    def set_trigger_slope(self, slope):
        self.device.trigger_slope = slope
        if self.is_device_connected():
            self.device.write_trigger_slope()

    def connect_to_device(self, port):
        if port == "":
            QMessageBox.about(
                self.main_window,
                "Connection failed",
                "Could not connect to device. No port is selected.",
            )
        elif port not in self.get_ports_names():
            QMessageBox.about(
                self.main_window,
                "Connection failed",
                f"Could not connect to device. Port {port} not available. Refresh and try again.",
            )
        else:
            self.device.connect(port)

    def disconnect_device(self):
        self.device.disconnect()

    def is_device_connected(self):
        return self.device.is_connected()

    def show_no_connection_message(self):
        QMessageBox.about(
            self.main_window,
            "Device not connected",
            "No device is connected. Connect a device first.",
        )

    def oscilloscope_single_run(self):
        if self.device.is_connected():
            self.continuous_acquisition = False
            self.device.clean_buffers()
            self.worker_wait_condition.notify_one()
            return True
        else:
            self.show_no_connection_message()
            return False

    def oscilloscope_continuous_run(self):
        if self.device.is_connected():
            self.timestamp_last_capture = time.time()
            self.spf = 1
            self.fps_timer.start(500)
            self.continuous_acquisition = True
            self.device.clean_buffers()
            self.worker_wait_condition.notify_one()
            return True
        else:
            self.show_no_connection_message()
            return False

    def oscilloscope_stop(self):
        self.continuous_acquisition = False
        self.fps_timer.stop()

    def data_ready_callback(self):
        curr_time = time.time()
        self.spf = 0.9 * (curr_time -
                          self.timestamp_last_capture) + 0.1 * self.spf
        self.timestamp_last_capture = curr_time
        self.main_window.screen.update_ch(self.data_time_array,
                                          self.acquisition_worker.data)
        if self.continuous_acquisition == True:
            self.worker_wait_condition.notify_one()

    def on_app_exit(self):
        print("exiting")
Beispiel #21
0
class SelectUser:
    def __init__(self, main_window):
        self.main_window = main_window
        self.cap = cv2.VideoCapture(0)
        self.image = None
        self.data_path = os.path.join(os.path.dirname(__file__), 'Data')
        self.detector = dlib.get_frontal_face_detector()
        self.predictor = dlib.shape_predictor(
            os.path.join(self.data_path,
                         'shape_predictor_68_face_landmarks.dat'))
        self.face_rec = dlib.face_recognition_model_v1(
            os.path.join(self.data_path,
                         'dlib_face_recognition_resnet_model_v1.dat'))
        self.names = ['Даня', 'Никита', 'Белла', 'Вадимчик']
        self.fdi = []
        self.ind_d = 0
        self.ind_u = 0
        self.ind_p = 0
        self.j = 0
        self.timer = QTimer()
        self.timer.timeout.connect(self.view_cam)
        self.shape = None

    def select_user(self):
        self.main_window.ui = Ui_SelectUser()
        self.main_window.ui.setupUi(self.main_window)
        self.main_window.ui.pushButton.clicked.connect(self.next_window)
        self.main_window.setWindowTitle('Обнаружение пользователя')
        self.timer.start(20)

    def view_cam(self):
        ret, self.image = self.cap.read()
        self.image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)
        height, width, channel = self.image.shape
        step = channel * width
        self.rectangle_face()
        q_img = QImage(self.image.data, width, height, step,
                       QImage.Format_RGB888)
        self.main_window.ui.label.setPixmap(QPixmap.fromImage(q_img))

    def rectangle_face(self):
        detection = self.detector(self.image, 1)
        face_descriptor_frame = 0
        if len(detection) == 0 and self.ind_d == 0:
            self.main_window.ui.label_2.setText('Нет никого в кадре')
            self.ind_d, self.ind_p, self.ind_u = 1, 0, 0
        for k, d in enumerate(detection):
            if self.j % 3 == 0:
                self.shape = self.predictor(self.image, d)
                face_descriptor_frame = self.face_rec.compute_face_descriptor(
                    self.image, self.shape)
            utils.rectangle_face(self.image, detection, self.shape)
            if self.j % 3 == 0:
                if not self.ind_p:
                    for c, fd in enumerate(self.fdi):
                        q = distance.euclidean(fd, face_descriptor_frame)
                        if q < 0.6:
                            self.main_window.ui.label_2.setText(
                                f'Привет {self.names[c]}!')
                            self.ind_p = 1
                            self.ind_d = 0
                            self.ind_u = 1

                if not self.ind_u:
                    self.main_window.ui.label_2.setText(
                        'Пользователь не определен')
                    self.ind_u = 1
                    self.ind_d = 0
        cv2.waitKey(5)
        self.j += 1

    def next_window(self):
        self.timer.stop()
        self.main_window.main_window.main_window()
class MainWindow(QMainWindow):
    """
    Main application entry-point for Wa-Tor.
    """
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self._playing = False
        self._tickpause = 50
        self._size = QSize(80, 40)
        self._settings = Settings()
        self._world = World(self._size, self._settings)

        self._ticks = 0
        self._updater = QTimer(self)
        self._updater.timeout.connect(self._tick)

        self.setWindowTitle("Planet Wa-Tor")
        self.setWindowIcon(QIcon("res/icon_wator.png"))
        self.setCentralWidget(QWidget())

        self._wator_widget = WaTorWidget(self._world)
        self._wator_graph = WaTorGraph(self._world)
        self.home()

    def home(self):
        """
        Add the GUI elements to the window that represent the home state of the application.
        """
        toolbar = self.addToolBar("File")
        play = QAction(QIcon("res/icon_play.png"), "Play", self)
        toolbar.addAction(play)
        pause = QAction(QIcon("res/icon_pause.png"), "Pause", self)
        toolbar.addAction(pause)
        toolbar.addSeparator()
        quit_app = QAction(QIcon("res/icon_quit.png"), "Quit", self)
        toolbar.addAction(quit_app)
        toolbar.addSeparator()
        reset = QAction(QIcon("res/icon_reset.png"), "Reset", self)
        toolbar.addAction(reset)
        toolbar.actionTriggered[QAction].connect(self.toolbar_pressed)

        layout = QVBoxLayout()
        layout.addWidget(self._wator_widget)
        layout.addSpacing(10)

        slider_layout = QHBoxLayout()
        slider_layout.addWidget(QLabel("Tick Speed", self))
        slider = QSlider()
        slider.setTickPosition(QSlider.TicksBothSides)
        slider.setTickInterval(25)
        slider.setRange(0, 500)
        slider.setSingleStep(1)
        slider.setOrientation(Qt.Horizontal)
        slider.valueChanged.connect(self._set_tick_value)
        slider.setValue(self._tickpause)
        slider_layout.addWidget(slider)
        layout.addLayout(slider_layout)

        layout.addSpacing(10)
        layout.addWidget(self._wator_graph)

        self.centralWidget().setLayout(layout)

    def _quit(self):
        """
        Quit the application.
        """
        QtCore.QCoreApplication.instance().quit()

    def play(self):
        """
        Start running (or resume) the simulation.
        """
        self._updater.start(self._tickpause * 5)
        self._playing = True

    def pause(self):
        """
        Pause the running of the simulation.
        """
        self._playing = False
        self._updater.stop()

    def reset(self):
        """
        Reset the application.
        """
        self._ticks = 0
        self.pause()
        if self._settings.exec_() == QDialog.Accepted:
            self._world.reset(self._settings)
            self._wator_widget.repaint()
            self._wator_graph.reset()

    def toolbar_pressed(self, action):
        """
        Handle a button being pressed on the toolbar.
        """
        actions = {
            "Play": self.play,
            "Pause": self.pause,
            "Quit": self._quit,
            "Reset": self.reset
        }
        actions[action.text()]()

    def _set_tick_value(self, value):
        self._tickpause = value
        if self._playing:
            self.pause()
            self.play()

    def _tick(self):
        """
        Tick the world simulation.
        """
        self._ticks += 1
        self._world.update(self._ticks)
        self._wator_widget.repaint()
        self._wator_graph.repaint()

        fish, sharks = self._world.stats()
        if fish == 0 and sharks == 0:
            print("Both sharks and fish have become extinct.")
            self.pause()
        elif fish == 0 and sharks > 0:
            print("No more fish. Wa-Tor is overrun with sharks.")
            self.pause()
        elif sharks == 0:
            print("No more sharks. Wa-Tor will become overrun with fish.")
            self.pause()
Beispiel #23
0
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()
Beispiel #24
0
class RenderWindow(QWindow):
    def __init__(self, format):
        super(RenderWindow, self).__init__()
        self.setSurfaceType(QWindow.OpenGLSurface)
        self.setFormat(format)
        self.context = QOpenGLContext(self)
        self.context.setFormat(self.requestedFormat())
        if not self.context.create():
            raise Exception("Unable to create GL context")
        self.program = None
        self.timer = None
        self.angle = 0

    def initGl(self):
        self.program = QOpenGLShaderProgram(self)
        self.vao = QOpenGLVertexArrayObject()
        self.vbo = QOpenGLBuffer()

        format = self.context.format()
        useNewStyleShader = format.profile() == QSurfaceFormat.CoreProfile
        # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile
        # concept 3.2+ has. This may still fail since version 150 (3.2) is
        # specified in the sources but it's worth a try.
        if (format.renderableType() == QSurfaceFormat.OpenGL
                and format.majorVersion() == 3 and format.minorVersion() <= 1):
            useNewStyleShader = not format.testOption(
                QSurfaceFormat.DeprecatedFunctions)

        vertexShader = vertexShaderSource if useNewStyleShader else vertexShaderSource110
        fragmentShader = fragmentShaderSource if useNewStyleShader else fragmentShaderSource110
        if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex,
                                                    vertexShader):
            raise Exception("Vertex shader could not be added: {} ({})".format(
                self.program.log(), vertexShader))
        if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment,
                                                    fragmentShader):
            raise Exception(
                "Fragment shader could not be added: {} ({})".format(
                    self.program.log(), fragmentShader))
        if not self.program.link():
            raise Exception("Could not link shaders: {}".format(
                self.program.log()))

        self.posAttr = self.program.attributeLocation("posAttr")
        self.colAttr = self.program.attributeLocation("colAttr")
        self.matrixUniform = self.program.uniformLocation("matrix")

        self.vbo.create()
        self.vbo.bind()
        self.verticesData = vertices.tobytes()
        self.colorsData = colors.tobytes()
        verticesSize = 4 * vertices.size
        colorsSize = 4 * colors.size
        self.vbo.allocate(VoidPtr(self.verticesData),
                          verticesSize + colorsSize)
        self.vbo.write(verticesSize, VoidPtr(self.colorsData), colorsSize)
        self.vbo.release()

        vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
        if self.vao.isCreated():  # have VAO support, use it
            self.setupVertexAttribs()

    def setupVertexAttribs(self):
        self.vbo.bind()
        self.program.setAttributeBuffer(self.posAttr, GL.GL_FLOAT, 0, 2)
        self.program.setAttributeBuffer(self.colAttr, GL.GL_FLOAT,
                                        4 * vertices.size, 3)
        self.program.enableAttributeArray(self.posAttr)
        self.program.enableAttributeArray(self.colAttr)
        self.vbo.release()

    def exposeEvent(self, event):
        if self.isExposed():
            self.render()
            if self.timer is None:
                self.timer = QTimer(self)
                self.timer.timeout.connect(self.slotTimer)
            if not self.timer.isActive():
                self.timer.start(10)
        else:
            if self.timer and self.timer.isActive():
                self.timer.stop()

    def render(self):
        if not self.context.makeCurrent(self):
            raise Exception("makeCurrent() failed")
        functions = self.context.functions()
        if self.program is None:
            functions.glEnable(GL.GL_DEPTH_TEST)
            functions.glClearColor(0, 0, 0, 1)
            self.initGl()

        retinaScale = self.devicePixelRatio()
        functions.glViewport(0, 0,
                             self.width() * retinaScale,
                             self.height() * retinaScale)
        functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)

        self.program.bind()
        matrix = QMatrix4x4()
        matrix.perspective(60, 4 / 3, 0.1, 100)
        matrix.translate(0, 0, -2)
        matrix.rotate(self.angle, 0, 1, 0)
        self.program.setUniformValue(self.matrixUniform, matrix)

        if self.vao.isCreated():
            self.vao.bind()
        else:  # no VAO support, set the vertex attribute arrays now
            self.setupVertexAttribs()

        functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3)

        self.vao.release()
        self.program.release()

        # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block
        # and wait for vsync.
        self.context.swapBuffers(self)
        self.context.doneCurrent()

    def slotTimer(self):
        self.render()
        self.angle += 1

    def glInfo(self):
        if not self.context.makeCurrent(self):
            raise Exception("makeCurrent() failed")
        functions = self.context.functions()
        text = """Vendor: {}\nRenderer: {}\nVersion: {}\nShading language: {}
\nContext Format: {}\n\nSurface Format: {}""".format(
            functions.glGetString(GL.GL_VENDOR),
            functions.glGetString(GL.GL_RENDERER),
            functions.glGetString(GL.GL_VERSION),
            functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION),
            print_surface_format(self.context.format()),
            print_surface_format(self.format()))
        self.context.doneCurrent()
        return text
Beispiel #25
0
class EvelynDesktop(QStackedWidget):
    INTERVAL_SECS = 30
    ALERT_SECS = 5

    signal_get_ping = Signal()
    signal_post_history = Signal(int, QDateTime)

    def __init__(
            self,
            config_file: str
    ) -> None:
        super().__init__()
        # load config
        try:
            self.config = Config(config_file)
        except Exception as e:
            QMessageBox.critical(self, 'Config error', str(e))
            QTimer.singleShot(0, self.close)
            return
        # load settings
        self.settings = Settings()
        # state
        self.state_key: Optional[int] = None
        # label widget
        self.label_ping = ClickableLabel('Loading ...', self.post_history)
        self.label_ping.setTextFormat(Qt.RichText)
        self.label_ping.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        layout_ping = QGridLayout()
        layout_ping.setContentsMargins(0, 0, 0, 0)
        layout_ping.addWidget(self.label_ping)
        self.widget_ping = QWidget()
        self.widget_ping.setLayout(layout_ping)
        self.addWidget(self.widget_ping)
        # alert widget
        self.label_alert = QLabel()
        self.label_alert.setWordWrap(True)
        self.label_alert.setAlignment(Qt.AlignCenter)
        self.label_alert.setStyleSheet(f'background: #dddddd;')
        self.addWidget(self.label_alert)
        # context menu
        self.action_report_done = QAction('Report done ...')
        self.action_report_done.triggered.connect(self.report_done)
        self.action_exit = QAction('Exit')
        self.action_exit.triggered.connect(self.close)
        self.action_frameless = QAction('Frameless window')
        self.action_frameless.setCheckable(True)
        self.action_frameless.triggered.connect(self.set_frameless_window)
        self.action_homepage = QAction('Open homepage')
        self.action_homepage.triggered.connect(self.open_homepage)
        self.context_menu = QMenu()
        self.context_menu.addAction(self.action_report_done)
        self.context_menu.addAction(self.action_exit)
        self.context_menu.addAction(self.action_frameless)
        self.context_menu.addAction(self.action_homepage)
        # threads
        self.thread_communication = QThread()
        self.thread_communication.start()
        # workers
        self.worker_communication = CommunicationWorker(
            netloc=self.config.netloc,
            base_path=self.config.base_path,
            api_key=self.config.api_key,
            guild=self.config.guild,
            member=self.config.member)
        self.worker_communication.moveToThread(self.thread_communication)
        # signals
        self.worker_communication.signal_get_ping_done.connect(self.get_ping_done)
        self.worker_communication.signal_post_history_done.connect(self.post_history_done)
        self.signal_get_ping.connect(self.worker_communication.get_ping)
        self.signal_post_history.connect(self.worker_communication.post_history)
        # get ping timer
        QTimer.singleShot(0, self.get_ping)
        self.timer_ping = QTimer()
        self.timer_ping.timeout.connect(self.get_ping)
        self.timer_ping.setTimerType(Qt.VeryCoarseTimer)
        self.timer_ping.start(self.INTERVAL_SECS * 1000)
        # switch label timer
        self.timer_label = QTimer()
        self.timer_label.timeout.connect(lambda: self.setCurrentWidget(self.widget_ping))
        self.timer_label.setSingleShot(True)
        self.timer_label.setTimerType(Qt.CoarseTimer)
        # window attributes
        size = self.settings.get('window', 'size', type_=QSize)
        if size is not None:
            self.resize(size)
        pos = self.settings.get('window', 'pos', type_=QPoint)
        if pos is not None:
            self.move(pos)
        frameless = self.settings.get('window', 'frameless', type_=bool)
        if frameless is not None and frameless:
            QTimer.singleShot(100, self.action_frameless.trigger)
        self.setWindowFlag(Qt.WindowStaysOnTopHint, self.config.window_stays_on_top)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowTitle('Evelyn Reminder')

    def closeEvent(
            self,
            event: QCloseEvent
    ) -> None:
        # save settings
        with suppress_and_log_exception():
            self.settings.set('window', 'size', self.size())
            self.settings.set('window', 'pos', self.pos())
            self.settings.set('window', 'frameless', bool(self.windowFlags() & Qt.FramelessWindowHint))
        # stop communication thread
        with suppress_and_log_exception():
            self.thread_communication.quit()
            self.thread_communication.wait()
        # done
        super().closeEvent(event)

    def contextMenuEvent(
            self,
            event: QContextMenuEvent
    ) -> None:
        self.context_menu.exec_(event.globalPos())

    @Slot()
    def get_ping(self) -> None:
        logging.info('Get ping ...')
        self.signal_get_ping.emit()

    @Slot(int, str, str)
    def get_ping_done(
            self,
            key: int,
            text: str,
            color: str
    ) -> None:
        logging.info('Get ping done')
        if key == -1:
            self.state_key = None
            self.label_ping.setWordWrap(True)
        else:
            self.state_key = key
            self.label_ping.setWordWrap(False)
        self.label_ping.setText(text)
        self.widget_ping.setStyleSheet(f'background : {color}; ')

    @Slot()
    def post_history(
            self,
            date_time: QDateTime = QDateTime()
    ) -> None:
        # this method is called as Slot by ClickableLabel.mouseReleaseEvent() without arguments
        # this method is called directly by EvelynDesktop.report_done() with a date_time
        if self.state_key is None:
            return
        logging.info('Post history ...')
        self.label_alert.setText('Sending ...')
        self.label_alert.setStyleSheet(f'background: #dddddd;')
        self.setCurrentWidget(self.label_alert)
        self.signal_post_history.emit(self.state_key, date_time)

    @Slot(str, bool)
    def post_history_done(
            self,
            text: str,
            error: bool
    ) -> None:
        logging.info('Post history done')
        self.label_alert.setText(text)
        if error:
            self.label_alert.setStyleSheet(f'background: #dd4b4b;')
        self.timer_label.start(self.ALERT_SECS * 1000)
        # trigger instant ping update to avoid outdated info
        self.timer_ping.stop()
        self.timer_ping.start(self.INTERVAL_SECS * 1000)
        self.get_ping()

    @Slot()
    def report_done(self) -> None:
        self.timer_ping.stop()  # stop ping update while dialog is open
        report_done_dialog = ReportDoneDialog(self)
        response = report_done_dialog.exec()
        if response != QDialog.Accepted:
            self.timer_ping.start(self.INTERVAL_SECS * 1000)
            self.get_ping()
            return
        date_time = report_done_dialog.get_date_time()
        self.post_history(date_time)

    @Slot(bool)
    def set_frameless_window(
            self,
            value: bool
    ) -> None:
        pos = self.pos()
        self.setWindowFlag(Qt.FramelessWindowHint, value)
        # workaround: window goes invisible otherwise
        self.setVisible(True)
        # workaround: window would move up otherwise
        if value:
            QTimer.singleShot(100, lambda: self.move(pos))

    @Slot()
    def open_homepage(self) -> None:
        webbrowser.open('https://github.com/stefs/evelyn-reminder')