예제 #1
0
파일: Flow.py 프로젝트: not-undefined/Ryven
    def paste(self):
        data = {}
        try:
            data = json.loads(QGuiApplication.clipboard().text())
        except Exception as e:
            return

        self.clear_selection()

        # calculate offset
        positions = []
        for d in data['drawings']:
            positions.append({'x': d['pos x'], 'y': d['pos y']})
        for n in data['nodes']:
            positions.append({'x': n['position x'], 'y': n['position y']})

        offset_for_middle_pos = QPointF(0, 0)
        if len(positions) > 0:
            rect = QRectF(positions[0]['x'], positions[0]['y'], 0, 0)
            for p in positions:
                x = p['x']
                y = p['y']
                if x < rect.left():
                    rect.setLeft(x)
                if x > rect.right():
                    rect.setRight(x)
                if y < rect.top():
                    rect.setTop(y)
                if y > rect.bottom():
                    rect.setBottom(y)

            offset_for_middle_pos = self.last_mouse_move_pos - rect.center()

        self.undo_stack.push(Paste_Command(self, data, offset_for_middle_pos))
예제 #2
0
 def get_points_rect(self):
     rect = QRectF()
     left = 1
     right = -1
     up = 1
     down = -1
     for p in self.points:
         if p.x() < left:
             left = p.x()
         if p.x() > right:
             right = p.x()
         if p.y() < up:
             up = p.y()
         if p.y() > down:
             down = p.y()
     rect.setLeft(left)
     rect.setRight(right)
     rect.setTop(up)
     rect.setBottom(down)
     self.width = rect.width()
     self.height = rect.height()
     # rect.setLeft(-self.width/2)
     # rect.setRight(self.width/2)
     # rect.setTop(-self.height/2)
     # rect.setBottom(self.height/2)
     return rect
예제 #3
0
    def boundingRect(self):
        anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor))
        rect = QRectF()
        rect.setLeft(min(self._rect.left(), anchor.x()))
        rect.setRight(max(self._rect.right(), anchor.x()))
        rect.setTop(min(self._rect.top(), anchor.y()))
        rect.setBottom(max(self._rect.bottom(), anchor.y()))

        return rect
예제 #4
0
    def boundingRect(self):
        anchor = self.mapFromParent(self._chart.mapToPosition(self._anchor))
        rect = QRectF()
        rect.setLeft(min(self._rect.left(), anchor.x()))
        rect.setRight(max(self._rect.right(), anchor.x()))
        rect.setTop(min(self._rect.top(), anchor.y()))
        rect.setBottom(max(self._rect.bottom(), anchor.y()))

        return rect
예제 #5
0
 def boundingRect(self):
     # remember: (0, 0) shall be the NI's center!
     rect = QRectF()
     w = self.layout.geometry().width()
     h = self.layout.geometry().height()
     rect.setLeft(-w / 2)
     rect.setTop(-h / 2)
     rect.setWidth(w)
     rect.setHeight(h)
     return rect
예제 #6
0
    def get_title_rect(self):
        title_rect_offset_factor = 0.56

        header_rect = self.get_header_rect()
        rect = QRectF()
        rect.setTop(header_rect.top() + (header_rect.height() / 2) *
                    (1 - title_rect_offset_factor))
        rect.setLeft(header_rect.left() + 10)
        rect.setHeight(header_rect.height() * title_rect_offset_factor)
        w = header_rect.width() * title_rect_offset_factor
        title_width = self.display_name_FM.width(
            get_longest_line(self.parent_node.title))
        rect.setWidth(w if w > title_width else title_width)
        return rect
예제 #7
0
    def get_whole_scene_img(self):
        self.hide_proxies()
        img = QImage(self.sceneRect().width() / self.total_scale_div, self.sceneRect().height() / self.total_scale_div,
                     QImage.Format_RGB32)
        img.fill(Qt.transparent)

        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        rect = QRectF()
        rect.setLeft(-self.viewport().pos().x())
        rect.setTop(-self.viewport().pos().y())
        rect.setWidth(img.rect().width())
        rect.setHeight(img.rect().height())
        # rect is right... but it only renders from the viewport's point down-and rightwards, not from topleft (0,0) ...
        self.render(painter, rect, rect.toRect())
        self.show_proxies()
        return img
예제 #8
0
class Apartment:
    def __init__(self, coppelia_, n_rooms):

        self.coppelia = coppelia_

        self.num_rooms = n_rooms
        self.max_rooms_per_side = math.ceil(self.num_rooms / 2)

        self.initial_corridor_width = -1
        self.initial_corridor_height = -1

        self.initial_corridor = QRectF()

        self.fixed_height = random.uniform(4, 6)

        # Almacena los indices de las habitaciones que tendrán a su izquierda un pasillo
        self.dict_corridors_index_per_side = {'bottom': [], 'top': []}
        self.dict_rooms_per_side = {'bottom': [], 'top': []}
        self.dict_rooms_and_corridors_per_side = {'bottom': [], 'top': []}

        # Lista final una vez hechas todas las transformaciones
        self.total_rooms_and_corridors = []

        self.create_initial_corridor()
        self.select_side_corridors()

        self.get_random_rooms()
        self.adjust_rooms()  # to avoid narrow corridors

        self.add_doors()
        self.center_apartment()
        self.add_floor_per_room()
        self.add_walls()

    def create_initial_corridor(self):
        self.initial_corridor_height = random.uniform(1.5, 3)
        self.initial_corridor_width = random.uniform(self.num_rooms * 4 / 2,
                                                     self.num_rooms * 8 / 2)

        self.initial_corridor = QRectF(
            0, 0, self.initial_corridor_width, -self.initial_corridor_height
        )  # - height para que la parte de arriba sea el top
        self.initial_corridor.translate(-self.initial_corridor.center())

    def select_side_corridors(self):
        # -1 sin pasillo, 0 antes de la primera habitacion, 1 antes de la segunda
        corridor_position = np.arange(-1, self.max_rooms_per_side)

        possibles_corridors_per_side = round(self.max_rooms_per_side / 2)

        if possibles_corridors_per_side == 0:
            possibles_corridors_per_side = 1

        self.dict_corridors_index_per_side['top'] = random.sample(
            list(corridor_position), k=possibles_corridors_per_side)
        self.dict_corridors_index_per_side['bottom'] = random.sample(
            list(corridor_position), k=possibles_corridors_per_side)

        while -1 in self.dict_corridors_index_per_side['top']:
            self.dict_corridors_index_per_side['top'].remove(-1)

        while -1 in self.dict_corridors_index_per_side['bottom']:
            self.dict_corridors_index_per_side['bottom'].remove(-1)

        print('posicion pasillo parte superior',
              self.dict_corridors_index_per_side['top'])
        print('posicion pasillo parte inferior',
              self.dict_corridors_index_per_side['bottom'])

    def get_random_rooms(self):

        dict_opposite_side = {'bottom': 'top', 'top': 'bottom'}

        for i in range(0, self.num_rooms):

            random_side = random.choice(['top', 'bottom'])

            if len(self.dict_rooms_per_side[random_side]
                   ) >= self.max_rooms_per_side:
                random_side = dict_opposite_side[random_side]

            # El indice de mi habitacion está en la lista de pasillos por indice luego tengo que añadir un pasillo a
            # su izquierda
            if len(self.dict_rooms_per_side[random_side]
                   ) in self.dict_corridors_index_per_side[random_side]:
                self.add_corridor(random_side, self.initial_corridor_height,
                                  self.fixed_height)

            if len(self.dict_rooms_and_corridors_per_side[random_side]) == 0:

                if random_side == 'bottom':
                    initial_point = self.initial_corridor.bottomLeft()
                else:
                    initial_point = self.initial_corridor.topLeft() + QPointF(
                        0, self.fixed_height)

            else:
                initial_point = self.dict_rooms_and_corridors_per_side[
                    random_side][-1].room_qrect.topRight()

            room = Room(type='genericRoom',
                        p=initial_point,
                        w=random.uniform(4, 8),
                        h=-self.fixed_height)

            self.dict_rooms_and_corridors_per_side[random_side].append(room)
            self.dict_rooms_per_side[random_side].append(room)

        for room_location in ['top', 'bottom']:
            if len(self.dict_rooms_per_side[room_location]
                   ) in self.dict_corridors_index_per_side[room_location]:
                try:
                    if self.dict_rooms_and_corridors_per_side[room_location][
                            -1].type != 'corridor':
                        self.add_corridor(random_side,
                                          self.initial_corridor_height,
                                          self.fixed_height)
                except:
                    print('there isnt rooms in this side of the corridor')

    def add_corridor(self, side, corridor_width, corridor_height):

        if len(self.dict_rooms_and_corridors_per_side[side]) == 0:

            if side == 'bottom':
                initial_point = self.initial_corridor.bottomLeft()
            else:
                initial_point = self.initial_corridor.topLeft() + QPointF(
                    0, self.fixed_height)

        else:
            initial_point = self.dict_rooms_and_corridors_per_side[side][
                -1].room_qrect.topRight()

        corridor = Room(type='corridor',
                        p=initial_point,
                        w=corridor_width,
                        h=-corridor_height)

        self.dict_rooms_and_corridors_per_side[side].append(corridor)

    def adjust_rooms(self):

        if self.num_rooms == 1:
            return

        dict_side_width = {'bottom': 0., 'right': 0., 'top': 0., 'left': 0.}

        for side, rooms in self.dict_rooms_per_side.items():
            print(f' side {side} has {len(rooms)} rooms ')
            for room in rooms:
                r = room.room_qrect
                dict_side_width[side] += r.width()

        diff = abs(dict_side_width['top'] - dict_side_width['bottom'])

        dict_opposite_side = {
            'bottom': 'top',
            'right': 'left',
            'top': 'bottom',
            'left': 'right'
        }

        if dict_side_width['top'] > dict_side_width['bottom']:
            print('top side is longer')
            side_to_modify = 'bottom'
        else:
            print('bottom side is longer')
            side_to_modify = 'top'

        print(f'--- Modifying {side_to_modify} room ---')

        room_to_modify = self.dict_rooms_and_corridors_per_side[
            side_to_modify][-1]
        opposite_room = self.dict_rooms_and_corridors_per_side[
            dict_opposite_side[side_to_modify]][-1]

        my_side_right = room_to_modify.room_qrect.topRight()
        opposite_side_right = opposite_room.room_qrect.topRight()

        if room_to_modify.type == 'corridor':
            print(f' Room of type {room_to_modify.type} ')

            room_to_modify.room_qrect.setTopRight(
                QPointF(opposite_side_right.x(), my_side_right.y()))

            self.dict_rooms_and_corridors_per_side[side_to_modify][
                -1] = room_to_modify
            self.dict_rooms_and_corridors_per_side[side_to_modify][
                -1].update_room_dimensions()

        else:
            if diff < self.initial_corridor_height:
                print('widening room')
                num_corridors_to_add = 0
            else:
                print('widening room and creating corridor')
                num_corridors_to_add = 1

            print(
                f' Room of type {room_to_modify.type}  -- adding {num_corridors_to_add} corridors'
            )

            room_to_modify.room_qrect.setTopRight(
                QPointF(
                    opposite_side_right.x() -
                    num_corridors_to_add * self.initial_corridor_height,
                    my_side_right.y()))
            self.dict_rooms_and_corridors_per_side[side_to_modify][
                -1] = room_to_modify
            self.dict_rooms_and_corridors_per_side[side_to_modify][
                -1].update_room_dimensions()

            if num_corridors_to_add > 0:
                self.add_corridor(side=side_to_modify,
                                  corridor_width=num_corridors_to_add *
                                  self.initial_corridor_height,
                                  corridor_height=self.fixed_height)

    def add_doors(self):
        opposite = {'bottom': 'top', 'top': 'bottom'}

        for current_side, rooms in self.dict_rooms_per_side.items():

            for i, room in enumerate(rooms):

                possibles_door_locations = [opposite[current_side]]

                if i in self.dict_corridors_index_per_side[
                        current_side]:  # Pasillo a la izquierda
                    possibles_door_locations.append('left')

                if i + 1 in self.dict_corridors_index_per_side[current_side]:
                    possibles_door_locations.append('right')

                door_location = random.choice(possibles_door_locations)
                room.add_door(door_location)

    def center_apartment(self):
        union_polygon = QPolygonF()

        for list in self.dict_rooms_and_corridors_per_side.values():
            for room in list:
                union_polygon = union_polygon.united(
                    room.room_qpolygon)  # Para obtener el bounding box
                self.total_rooms_and_corridors.append(room)

        self.initial_corridor.setLeft(union_polygon.boundingRect().left())
        self.initial_corridor.setRight(union_polygon.boundingRect().right())

        self.total_rooms_and_corridors.append(
            Room(type='corridor',
                 p=self.initial_corridor.topLeft(),
                 w=self.initial_corridor.width(),
                 h=self.initial_corridor.height()))

        union_polygon = union_polygon.united(self.initial_corridor)

        initial_center = union_polygon.boundingRect().center()
        union_polygon.translate(-initial_center)

        self.apartment_boundingRect = union_polygon.boundingRect()

        # Desplazo habitaciones y pasillos al centro
        for i, room in enumerate(self.total_rooms_and_corridors):
            room.room_qpolygon.translate(
                -initial_center
            )  # Desplazo los poligonos para que la habitación esté centrada
            room.room_qrect.translate(-initial_center)

    def add_walls(self):

        for i, room in enumerate(self.total_rooms_and_corridors):
            walls = []
            if room.type == 'corridor':
                continue

            polygon = room.room_qpolygon

            prev_point = polygon[0]
            for i, curr_point in enumerate(polygon):
                if i == 0:
                    continue
                walls.append(([prev_point.x(),
                               prev_point.y(),
                               .425], [curr_point.x(),
                                       curr_point.y(), .425]))
                prev_point = curr_point

            room.walls = walls

            wall_thread = WallCreator(data, walls)
            wall_thread.start()

        walls = []

        polygon_br = QPolygonF(self.apartment_boundingRect, closed=True)
        prev_point_br = polygon_br[0]
        for i, curr_point_br in enumerate(polygon_br):
            if i == 0:
                continue

            walls.append(([prev_point_br.x(),
                           prev_point_br.y(),
                           .4], [curr_point_br.x(),
                                 curr_point_br.y(), .4]))
            prev_point_br = curr_point_br

        wall_thread = WallCreator(data, walls)
        wall_thread.start()

    def add_floor(self):  # un suelo conjunto para el apartamento

        fscale_x = self.apartment_boundingRect.width() / 5 + 0.5
        fscale_y = self.apartment_boundingRect.height() / 5 + 0.5

        # Create and scale a floor
        r = self.coppelia.create_model(
            'models/infrastructure/floors/5mX5m wooden floor.ttm', 0, 0, 0, 0)

        self.coppelia.scale_object(r, fscale_x, fscale_y, 1)
        for handle in self.coppelia.get_objects_children(r):
            self.coppelia.scale_object(handle, fscale_x, fscale_y, 1)

    def add_floor_per_room(self):

        for room in self.total_rooms_and_corridors:

            room_boundingRect = room.room_qpolygon.boundingRect()
            room_center = room_boundingRect.center()

            fscale_x = room_boundingRect.width() / 5
            fscale_y = room_boundingRect.height() / 5

            if room.type == 'corridor':
                floor = self.coppelia.create_model(
                    'models/infrastructure/floors/5mX5m wooden floor.ttm',
                    room_center.x(), room_center.y(), 0, 0)
            else:
                floor = self.coppelia.create_model(
                    'models/infrastructure/floors/5mX5m concrete floor.ttm',
                    room_center.x(), room_center.y(), 0, 0)

            self.coppelia.scale_object(floor, fscale_x, fscale_y, 1)
class QSlideNavigationBar(QWidget):
    class ItemLineStyle(Enum):
        ItemNone = 1
        ItemTop = 2
        ItemRight = 3
        ItemBottom = 4
        ItemLeft = 5
        ItemRect = 6

    itemClicked = Signal(int, str)

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

        # -------成员变量定义------------#
        # ==========属性=========#
        self._m_bar_start_color = QColor('#511235')  # type: QColor  # 导航栏起始颜色
        self._m_bar_end_color = QColor('#150507')  # type: QColor  # 导航栏结束颜色
        self._m_bar_radius = 0  # type: int     # 导航栏四个角的圆弧半径
        self._m_item_start_color = QColor(255, 255, 255, 50)  # type: QColor  # item 的起始颜色
        self._m_item_end_color = QColor("black")  # type: QColor  # item 的结束颜色
        self._m_current_hover_index = -1  # type: int    # 当前光标所在的item的index
        self._m_item_hover_start_color = QColor(255, 0, 0, 25)  # type: QColor   # 光标所在的item 的起始颜色
        self._m_item_hover_end_color = QColor(255, 0, 255, 25)  # type: QColor
        self._m_item_text_color = QColor("red")  # type: QColor  # item 的文字颜色
        self._m_item_line_color = QColor("red")  # type: QColor  # item 的线的颜色
        self._m_item_line_width = 5  # type: int     # 线的宽度
        self._m_item_line_style = self.ItemLineStyle.ItemNone  # type: QSlideNavigationBar.ItemLineStyle  # 线的样式类型
        self._m_item_font = QFont('宋体')  # type: QFont   # 字体家族
        self._m_item_font_size = 16  # type: int     # 字体大小
        self._m_item_radius = 0  # type: int     # item 的圆角半径
        self._m_space = 40  # type: int     # 间距大小, item 背景大小
        self._m_orientation = Qt.Horizontal  # type: Qt.Orientation  # 导航栏的方向:横向,纵向
        self._m_enable_key_move = True  # type: bool    # 是否可以使用按键切换item
        self._m_fixed = False  # type: bool    # 大小固定
        self._m_slide_velocity = 10  # type: int     # 滑动速度
        self._m_shake_velocity = 10  # type: int     # 晃动速度

        # ===========内部变量=========#
        self._m_item_maps = {}  # type: map(int, list(str, QRectF)) # 保存的item列表
        self._m_total_text_width = 0  # type: int     # 总的文字的宽度
        self._m_total_text_height = 0  # type: int     # 总的文字的高度
        self._m_current_index = 0  # type: int     # 当前选中的item 的索引
        self._m_start_rect = QRectF()  # type:QRectF  #起始矩形
        self._m_stop_rect = QRectF()  # type: QRectF  # 结束矩形
        self._m_slide_timer = QTimer(self)  # type: QTimer  # 滑动的定时器
        self._m_shake_timer = QTimer(self)  # type: QTimer  # 晃动的定时器
        self._m_forward = False  # type: bool    # 前进

        # ----------执行初始化动作-------------#
        self.setAttribute(Qt.WA_TranslucentBackground)
        self._m_slide_timer.setInterval(self._m_slide_velocity)
        self._m_slide_timer.timeout.connect(self._on_do_slide)
        self._m_shake_timer.setInterval(self._m_shake_velocity)
        self._m_shake_timer.timeout.connect(self._on_do_shake)

        self.setFocusPolicy(Qt.ClickFocus)
        self.setMouseTracking(True)

    # ---------以下是 API 接口----------#
    def add_item(self, item_str: str):
        """
        向导航栏中添加项目(代表一个选项卡),添加之前会查重
        :param item_str:项目名称(显示出来的文字)
        :return:None
        """
        if not item_str:
            return

        for key, value in self._m_item_maps.items():
            if value[0] == item_str:
                return  # 如果存在同名item,则返回

        f = QFont()
        f.setPointSize(self._m_item_font_size)
        fm = QFontMetrics(f)

        text_width = fm.width(item_str)
        text_height = fm.height()
        item_count = len(self._m_item_maps)
        if item_count > 0:
            if self._m_orientation == Qt.Horizontal:
                top_left = QPointF(self._m_total_text_width, 0)
                self._m_total_text_width += text_width + self._m_space
                bottom_right = QPointF(self._m_total_text_width, self._m_total_text_height)
            else:
                top_left = QPointF(0, self._m_total_text_height)
                self._m_total_text_height += text_height + self._m_space
                bottom_right = QPointF(self._m_total_text_width, self._m_total_text_height)

            self._m_item_maps[item_count] = [item_str, QRectF(top_left, bottom_right)]
        else:
            if self._m_orientation == Qt.Horizontal:
                # 水平方向,水平各占1个space, 竖直占1个space
                self._m_total_text_width = text_width + self._m_space
                self._m_total_text_height = text_height + self._m_space
            else:
                # 竖直方向, 水平各占2个space, 竖直占一个space
                self._m_total_text_width = text_width + 2 * self._m_space
                self._m_total_text_height = text_height + self._m_space

            top_left = QPointF(0.0, 0.0)
            bottom_right = QPointF(self._m_total_text_width, self._m_total_text_height)
            self._m_item_maps[item_count] = [item_str, QRectF(top_left, bottom_right)]
        self.setMinimumSize(self._m_total_text_width, self._m_total_text_height)

        if self._m_fixed:
            if self._m_orientation == Qt.Horizontal:
                self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)  # 固定高度
            else:
                self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)  # 固定宽度
        if len(self._m_item_maps):
            self._m_start_rect = QRectF(self._m_item_maps[0][1])
        self.update()

    def set_items(self, items_list: list):
        """
        一次性设置一组项目, 会清空之前设置的项目
        :param items_list: 项目名称列表
        :return: None
        """
        pass

    def get_items(self):
        pass

    def set_bar_start_color(self, color: QColor):
        """
        设置导航栏背景的起始颜色(渐变色的起始)
        :param color:QColor类型
        :return: None
        """
        if color != self._m_bar_start_color:
            self._m_bar_start_color = color
            self.update()

    def set_bar_end_color(self, color: QColor):
        """
        设置导航栏背景的结束颜色(渐变色的结束)
        :param color: QColor 类型
        :return: None
        """
        if color != self._m_bar_end_color:
            self._m_bar_end_color = color
            self.update()

    def set_item_start_color(self, color: QColor):
        """
        设置项目的背景色的起始颜色(渐变色的起始)
        :param color: QColor 类型
        :return: None
        """
        if color != self._m_item_start_color:
            self._m_item_start_color = color
            self.update()

    def set_item_end_color(self, color: QColor):
        """
        设置项目的背景色的结束颜色(渐变色的结束)
        :param color: QColor 类型
        :return: None
        """
        if color != self._m_item_end_color:
            self._m_item_end_color = color
            self.update()

    def set_item_text_color(self, color: QColor) -> None:
        """
        设置 item 的文字颜色
        :param color: QColor 类型
        :return:  None
        """
        if color != self._m_item_text_color:
            self._m_item_text_color = color
            self.update()

    def set_item_line_color(self, color: QColor) -> None:
        """
        设置 item 的线的颜色
        :param color: QColor 类型
        :return:  None
        """
        if color != self._m_item_line_color:
            self._m_item_line_color = color
            self.update()

    def set_bar_radius(self, radius: int):
        """
        设置导航栏四个角的圆弧半径
        :param radius: int 类型
        :return: None
        """
        if radius >= 0 and radius != self._m_bar_radius:
            self._m_bar_radius = radius
            self.update()

    def set_item_radius(self, radius: int):
        """
        设置项目的四个角的圆弧半径
        :param radius: int 类型
        :return: None
        """
        if radius >= 0 and radius != self._m_item_radius:
            self._m_item_radius = radius
            self.update()

    def set_space(self, space: int):
        """
        设置 item 所占的空间大小
        :param space: int 类型
        :return: None
        """
        if space >= 0 and space != self._m_space:
            self._m_space = space
            self.update()

    def set_item_line_width(self, width: int):
        """
        设置项目周围的线宽度
        :param width: int 类型
        :return: None
        """
        if width >= 0 and width != self._m_item_line_width:
            self._m_item_line_width = width
            self.update()

    def set_item_line_style(self, style: ItemLineStyle):
        """
        设置项目周围的线的类型:不显示,上方,下方,左方, 右方, 矩形
        :param style: 枚举类型
        :return: None
        """
        if style != self._m_item_line_style:
            self._m_item_line_style = style
            self.update()

    def set_orientation(self, orientation: Qt.Orientation):
        """
        设置导航栏是横向还是纵向
        :param orientation: Qt.Orientation 类型
        :return: None
        """
        if orientation != self._m_orientation:
            self._m_orientation = orientation
            self.update()

    def set_fixed(self, fixed: bool):
        """
        设置导航栏尺寸固定,不随窗口大小进行缩放
        :param fixed:  bool 类型
        :return:  None
        """
        if fixed != self._m_fixed:
            self._m_fixed = fixed
            self.update()

    def get_current_item_index(self):
        """
        返回当前选中项目的索引号
        :return: int类型,索引号
        """
        return self._m_current_index

    # -----------以下是槽函数--------------#
    def on_set_enable_key_move(self, enable: bool):
        """
        设置可以使用按键来切换导航项目
        :param enable: bool 类型
        :return: None
        """
        if enable != self._m_enable_key_move:
            self._m_enable_key_move = enable

    def on_move_to_first_item(self):
        """
        切换到第一个项目
        :return: None
        """
        self.on_move_to_index(0)

    def on_move_to_last_item(self):
        """
        切换到最后一个项目
        :return: None
        """
        self.on_move_to_index(len(self._m_item_maps) - 1)

    def on_move_to_previous_item(self):
        """
        切换到上一个项目
        :return: None
        """
        if self._m_current_index == 0:
            return
        self.on_move_to_index(self._m_current_index - 1)

    def on_move_to_next_item(self):
        """
        切换到下一个项目
        :return: None
        """
        if self._m_current_index == len(self._m_item_maps) - 1:
            return

        self.on_move_to_index(self._m_current_index + 1)

    def on_move_to_index(self, index: int):
        """
        切换到指定索引的项目
        :param index: int, 项目的索引号
        :return: None
        """
        if (index >= 0) and (index < len(self._m_item_maps)) and (index != self._m_current_index):
            self.itemClicked.emit(index, self._m_item_maps[index][0])
            if self._m_current_index == -1:
                self._m_start_rect = QRectF(self._m_item_maps[index][1])

            self._m_forward = index > self._m_current_index
            self._m_current_index = index
            self._m_stop_rect = QRectF(self._m_item_maps[index][1])
            self._m_slide_timer.start()

    def on_move_to_name(self, name: str):
        """
        切换到指定名称的项目
        :param name: 项目的名称,str类型
        :return: None
        """
        for key, value in self._m_item_maps.items():
            if value[0] == name:
                target_index = key
                if target_index == self._m_current_index:
                    return
                self.on_move_to_index(target_index)
                break

    def on_move_to_position(self, point: QPointF):
        """
        切换到指定位置的项目
        :param point: 位置坐标,QPointF类型
        :return: None
        """
        for key, value in self._m_item_maps.items():
            if value[1].contains(point):
                target_index = key
                if target_index == self._m_current_index:
                    return
                self.on_move_to_index(target_index)
                break

    def on_set_current_item_index(self, index: int):
        """
        将当前选中项目切换到指定索引的项目
        :param index: 索引号, int
        :return: NOne
        """
        self.on_move_to_index(index)

    # ------------以下是私有槽函数---------#
    def _on_do_slide(self):
        """
        完成滑动动作
        :return: None
        """
        if self._m_space <= 0 or self._m_start_rect == self._m_stop_rect:
            self.update()
            self._m_slide_timer.stop()
            return
        if self._m_orientation == Qt.Horizontal:
            dx = self._m_space / 2.0
            dy = 0
        else:
            dx = 0
            dy = self._m_space / 2.0

        if self._m_forward:
            self._m_start_rect.adjust(dx, dy, dx, dy)
            if ((self._m_orientation == Qt.Horizontal) and
                (self._m_start_rect.topLeft().x() >= self._m_stop_rect.topLeft().x())) or \
                    ((self._m_orientation == Qt.Vertical) and
                     (self._m_start_rect.topLeft().y() >= self._m_stop_rect.topLeft().y())):
                self._m_slide_timer.stop()
                if self._m_start_rect != self._m_stop_rect:
                    self._m_shake_timer.start()
        else:
            self._m_start_rect.adjust(-dx, -dy, -dx, -dy)
            if ((self._m_orientation == Qt.Horizontal) and
                (self._m_start_rect.topLeft().x() <= self._m_stop_rect.topLeft().x())) or \
                    ((self._m_orientation == Qt.Vertical) and
                     (self._m_start_rect.topLeft().y() <= self._m_stop_rect.topLeft().y())):
                self._m_slide_timer.stop()
                if self._m_start_rect != self._m_stop_rect:
                    self._m_shake_timer.start()
        self.update()

    def _on_do_shake(self):
        """
        完成晃动动作
        :return: None
        """
        delta = 2.0
        dx1 = dx2 = dy1 = dy2 = 0.0
        if self._m_start_rect.topLeft().x() > self._m_stop_rect.topLeft().x():
            dx1 = -delta
        elif self._m_start_rect.topLeft().x() < self._m_stop_rect.topLeft().x():
            dx1 = delta
        if self._m_start_rect.topLeft().y() > self._m_stop_rect.topLeft().y():
            dy1 = -delta
        elif self._m_start_rect.topLeft().y() < self._m_stop_rect.topLeft().y():
            dy1 = delta
        if self._m_start_rect.bottomRight().x() > self._m_stop_rect.bottomRight().x():
            dx2 = -delta
        elif self._m_start_rect.bottomRight().x() < self._m_stop_rect.bottomRight().x():
            dx2 = delta
        if self._m_start_rect.bottomRight().y() > self._m_stop_rect.bottomRight().y():
            dy2 = -delta
        elif self._m_start_rect.bottomRight().y() < self._m_stop_rect.bottomRight().y():
            dy2 = delta

        self._m_start_rect.adjust(dx1, dy1, dx2, dy2)

        if abs(self._m_start_rect.topLeft().x() - self._m_stop_rect.topLeft().x()) <= delta:
            self._m_start_rect.setLeft(self._m_stop_rect.topLeft().x())
        if abs(self._m_start_rect.topLeft().y() - self._m_stop_rect.topLeft().y()) <= delta:
            self._m_start_rect.setTop(self._m_stop_rect.topLeft().y())
        if abs(self._m_start_rect.bottomRight().x() - self._m_stop_rect.bottomRight().x()) <= delta:
            self._m_start_rect.setRight(self._m_stop_rect.bottomRight().x())
        if abs(self._m_start_rect.bottomRight().y() - self._m_stop_rect.bottomRight().y()) <= delta:
            self._m_start_rect.setBottom(self._m_stop_rect.bottomRight().y())
        if self._m_start_rect == self._m_stop_rect:
            self._m_shake_timer.stop()
        self.update()

    # -----------以下是事件响应函数的重载函数-----------#
    def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
        """
        重载paintEvent函数
        :param a0:
        :return:
        """
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        self._draw_bar_background(painter)
        self._draw_item_background(painter)
        self._draw_item_line(painter)
        self._draw_text(painter)

    def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
        """
        重载resizeEvent函数
        :param a0:
        :return:
        """
        self._adjust_item_size()

    def mousePressEvent(self, a0: QtGui.QMouseEvent) -> None:
        """
        重载mousePressEvent函数
        :param a0: 事件
        :return:None
        """
        self.on_move_to_position(a0.pos())

    def mouseMoveEvent(self, a0: QtGui.QMouseEvent) -> None:
        """
        重载mouseMoveEvent函数
        :param a0: 事件
        :return: None
        """
        is_on_item = False
        for key, value in self._m_item_maps.items():
            if value[1].contains(a0.pos()):
                self._m_current_hover_index = key
                is_on_item = True
                break
        if not is_on_item:
            self._m_current_hover_index = -1

        self.update()

    def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
        """
        重载keyPressEvent函数
        :param a0:
        :return:
        """
        if not self._m_enable_key_move:
            # self.keyPressEvent(a0)
            return
        if a0.key() == Qt.Key_Home:
            self.on_move_to_first_item()
        elif a0.key() == Qt.Key_End:
            self.on_move_to_last_item()
        elif a0.key() == Qt.Key_Up or a0.key() == Qt.Key_Left:
            self.on_move_to_previous_item()
        elif a0.key() == Qt.Key_Down or a0.key() == Qt.Key_Right:
            self.on_move_to_next_item()
        else:
            # self.keyPressEvent(a0)
            return

    # -----------------以下是私有成员函数---------------------#
    def _draw_bar_background(self, p: QPainter):
        """
        绘制导航栏的背景
        :param p: 画刷
        :return: None
        """
        p.save()
        p.setPen(Qt.NoPen)
        lgt = QLinearGradient(QPointF(0, 0), QPointF(0, self.height()))
        lgt.setColorAt(0.0, self._m_bar_start_color)
        lgt.setColorAt(1.0, self._m_bar_end_color)
        p.setBrush(lgt)
        p.drawRoundedRect(self.rect(), self._m_bar_radius, self._m_bar_radius)
        p.restore()

    def _draw_item_background(self, p: QPainter):
        """
        绘制项目的背景
        :param p: 画刷
        :return: None
        """
        if self._m_start_rect.isNull():
            return

        p.save()
        lgt = QLinearGradient(self._m_start_rect.topLeft(), self._m_start_rect.bottomRight())
        lgt.setColorAt(0.0, self._m_item_start_color)
        lgt.setColorAt(1.0, self._m_item_end_color)
        p.setPen(Qt.NoPen)
        p.setBrush(lgt)
        p.drawRoundedRect(self._m_start_rect, self._m_item_radius, self._m_item_radius)

        # 绘制 hover 状态下的item
        if self._m_current_hover_index != -1:
            hover_rect = QRectF(self._m_item_maps[self._m_current_hover_index][1])
            lgt = QLinearGradient(hover_rect.topLeft(), hover_rect.bottomRight())
            lgt.setColorAt(0.0, self._m_item_hover_start_color)
            lgt.setColorAt(1.0, self._m_item_hover_end_color)
            p.setPen(Qt.NoPen)
            p.setBrush(lgt)
            p.drawRoundedRect(hover_rect, self._m_item_radius, self._m_item_radius)
        p.restore()

    def _draw_item_line(self, p: QPainter) -> None:
        """
        绘制项目周边的线条
        :param p: 画刷
        :return: None
        """
        if self._m_start_rect.isNull():
            return

        if self._m_item_line_style == self.ItemLineStyle.ItemNone:
            return
        elif self._m_item_line_style == self.ItemLineStyle.ItemTop:
            p1 = self._m_start_rect.topLeft()
            p2 = self._m_start_rect.topRight()
        elif self._m_item_line_style == self.ItemLineStyle.ItemRight:
            p1 = self._m_start_rect.topRight()
            p2 = self._m_start_rect.bottomRight()
        elif self._m_item_line_style == self.ItemLineStyle.ItemBottom:
            p1 = self._m_start_rect.bottomLeft()
            p2 = self._m_start_rect.bottomRight()
        elif self._m_item_line_style == self.ItemLineStyle.ItemLeft:
            p1 = self._m_start_rect.topLeft()
            p2 = self._m_start_rect.bottomLeft()
        elif self._m_item_line_style == self.ItemLineStyle.ItemRect:
            p1 = self._m_start_rect.topLeft()
            p2 = self._m_start_rect.bottomRight()
        else:
            return

        p.save()
        line_pen = QPen()
        line_pen.setColor(self._m_item_line_color)
        line_pen.setWidth(self._m_item_line_width)
        p.setPen(line_pen)
        if self._m_item_line_style == self.ItemLineStyle.ItemRect:
            p.drawRoundedRect(QRectF(p1, p2), self._m_item_radius, self._m_item_radius)
        else:
            p.drawLine(p1, p2)

        p.restore()

    def _draw_text(self, p: QPainter) -> None:
        """
        绘制项目的名称
        :param p: 画刷
        :return: None
        """
        p.save()
        p.setPen(self._m_item_text_color)
        for key, value in self._m_item_maps.items():
            self._m_item_font.setPointSize(self._m_item_font_size)
            p.setFont(self._m_item_font)
            p.drawText(value[1], Qt.AlignCenter, value[0])
        p.restore()

    def _adjust_item_size(self) -> None:
        """
        调整Item大小
        :return:
        """

        if self._m_fixed:
            return

        item_count = len(self._m_item_maps)

        if self._m_orientation == Qt.Horizontal:
            add_width = 1.0 * (self.width() - self._m_total_text_width) / item_count
            add_height = 1.0 * (self.height() - self._m_total_text_height)
        else:
            add_width = 1.0 * (self.width() - self._m_total_text_width)
            add_height = 1.0 * (self.height() - self._m_total_text_height) / item_count

        dx = dy = 0.0
        for key, value in self._m_item_maps.items():
            # f = QFont()
            fm = QFontMetrics(self._m_item_font)
            text_width = fm.width(value[0])
            text_height = fm.height()
            if self._m_orientation == Qt.Horizontal:
                topLeft = QPointF(dx, 0)
                dx += text_width + self._m_space + add_width
                dy = self._m_total_text_height + add_height
            else:
                topLeft = QPointF(0, dy)
                dx = self._m_total_text_width + add_width
                dy += text_height + self._m_space + add_height

            bottomRight = QPointF(dx, dy)
            text_rect = QRectF(topLeft, bottomRight)
            self._m_item_maps[key] = [value[0], QRectF(text_rect)]
            if key == self._m_current_index:
                self._m_start_rect = text_rect
                self._m_stop_rect = text_rect

        self.update()