首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >创建自定义时间选择器小部件

创建自定义时间选择器小部件
EN

Stack Overflow用户
提问于 2021-03-31 00:27:14
回答 1查看 162关注 0票数 1

我需要创建一个小部件,用于挑选时间。QTimeEdit小部件看起来并不直观,也不是一个好的设计。所以我决定创建一个类似于智能手机中时间选择器的时间选择器。

我设法创建了一个时钟并单击,使指针(类似于图像中的指针)移动到当前单击的位置(注意:它并不完美,看起来仍然很糟糕)。我希望在制造内部时钟方面得到帮助。

下面是我的代码:

代码语言:javascript
运行
复制
from PyQt5 import QtWidgets, QtGui, QtCore
import math, sys


class ClockWidget(QtWidgets.QWidget):   # I want to be able to reuse this class for other programs also, so please don't hard code values of the list, start and end

    def __init__(self, start, end, lst=[], *args, **kwargs):
        super(ClockWidget, self).__init__(*args, **kwargs)

        self.lst = lst

        if not self.lst:
            self.lst = [*range(start, end)]

        self.index_start = 0  # tune this to move the letters in the circle
        self.pointer_angles_multiplier = 9  # just setting the default values

        self.current = None
        self.rects = []

    @property
    def index_start(self):
        return self._index_start

    @index_start.setter
    def index_start(self, index):
        self._index_start = index

    def paintEvent(self, event):

        self.rects = []

        painter = QtGui.QPainter(self)
        pen = QtGui.QPen()
        pen.setColor(QtCore.Qt.red)
        pen.setWidth(2)
        painter.setPen(pen)

        x, y = self.rect().x(), self.rect().y()
        width, height = self.rect().width(), self.rect().height()

        painter.drawEllipse(x, y, x + width, x + height)

        s, t, equal_angles, radius = self.angle_calc()
        radius -= 30

        pen.setColor(QtCore.Qt.green)
        pen.setWidth(2)
        painter.setPen(pen)

        """ pointer angle helps in determining to which position the pointer should be drawn"""
        self.pointer_x, self.pointer_y = s + ((radius-30) * math.cos(self.pointer_angles_multiplier * equal_angles)), t \
                + ((radius-30)  * math.sin(self.pointer_angles_multiplier * equal_angles))

        """ The pendulum like pointer """
        painter.drawLine(QtCore.QPointF(s, t), QtCore.QPointF(self.pointer_x, self.pointer_y))

        painter.drawEllipse(QtCore.QRectF(QtCore.QPointF(self.pointer_x - 20, self.pointer_y - 40),
                                          QtCore.QPointF(self.pointer_x + 30, self.pointer_y + 10)))

        pen.setColor(QtCore.Qt.blue)
        pen.setWidth(3)

        font = self.font()
        font.setPointSize(14)
        painter.setFont(font)

        painter.setPen(pen)

        """ Drawing the number around the circle formula y = t + radius * cos(a)
            y = s + radius * sin(a) where angle is in radians (s, t) are the mid point of the circle """
        for index, char in enumerate(self.lst, start=self.index_start):
            angle = equal_angles * index

            y = t + radius * math.sin(angle)
            x = s + radius * math.cos(angle)

            # print(f"Add: {add_x}, index: {index}; char: {char}")
            rect = QtCore.QRectF(x - 30, y - 40, x + 60, y)  # clickable point

            self.rects.append([index, char, rect])  # appends index, letter, rect

            painter.setPen(QtCore.Qt.blue)
            painter.drawRect(rect)  # helps in visualizing the points where the click can received

            print(f"Rect: {rect}; char: {char}")
            painter.setPen(QtCore.Qt.red)

            points = QtCore.QPointF(x, y)
            painter.drawText(points, str(char))

    def mousePressEvent(self, event):

        for x in self.rects:

            index, char, rect = x

            if event.button() & QtCore.Qt.LeftButton and rect.contains(event.pos()):

                self.pointer_angles_multiplier = index
                self.current = char
                self.update()
                break

    def angle_calc(self):
        """
         This will simply return (midpoints of circle, divides a circle into the len(list) and return the
         angle in radians, radius)
         """
        return ((self.rect().width() - self.rect().x()) / 2, (self.rect().height() - self.rect().y()) / 2,
                (360 / len(self.lst)) * (math.pi / 180), (self.rect().width() / 2))

    def resizeEvent(self, event: QtGui.QResizeEvent):
        """This is supposed to maintain a Square aspect ratio on widget resizing but doesn't work
        correctly as you will see when executing"""

        if event.size().width() > event.size().height():
            self.resize(event.size().height(), event.size().width())

        else:
            self.resize(event.size().width(), event.size().width())


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)

    message = ClockWidget(1, 13)
    message.index_start = 10
    message.show()

    sys.exit(app.exec())

输出:

蓝色矩形表示可单击区域。如果你也可以,让指针在时钟内单击时移动到最接近的数字(而不仅仅是在蓝色区域内单击时移动指针),我会很高兴。

在我的代码中还有一个问题,那就是数字与外部圆圈的间距不均匀。(比如数字12比数字6离外圈更近)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-03-31 01:31:50

免责声明:我不会解释错误的原因,但我认为我提供的代码应该对错误有一个清晰的解释。

逻辑是计算每个小圆的圆心位置,并使用外接矩形作为基础来绘制文本,并检查您单击的点是否靠近文本。

代码语言:javascript
运行
复制
from functools import cached_property
import math
import sys

from PyQt5 import QtCore, QtGui, QtWidgets


class ClockWidget(QtWidgets.QWidget):
    L = 12
    r = 40.0
    DELTA_ANGLE = 2 * math.pi / L
    current_index = 9

    def paintEvent(self, event):
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)

        R = min(self.rect().width(), self.rect().height()) / 2
        margin = 4

        Rect = QtCore.QRectF(0, 0, 2 * R - margin, 2 * R - margin)
        Rect.moveCenter(self.rect().center())

        painter.setBrush(QtGui.QColor("gray"))
        painter.drawEllipse(Rect)

        rect = QtCore.QRectF(0, 0, self.r, self.r)

        if 0 <= self.current_index < 12:
            c = self.center_by_index(self.current_index)
            rect.moveCenter(c)
            pen = QtGui.QPen(QtGui.QColor("red"))
            pen.setWidth(5)
            painter.setPen(pen)
            painter.drawLine(c, self.rect().center())

            painter.setBrush(QtGui.QColor("red"))
            painter.drawEllipse(rect)

        for i in range(self.L):
            j = (i + 2) % self.L + 1
            c = self.center_by_index(i)
            rect.moveCenter(c)
            painter.setPen(QtGui.QColor("white"))
            painter.drawText(rect, QtCore.Qt.AlignCenter, str(j))

    def center_by_index(self, index):
        R = min(self.rect().width(), self.rect().height()) / 2
        angle = self.DELTA_ANGLE * index
        center = self.rect().center()

        return center + (R - self.r) * QtCore.QPointF(math.cos(angle), math.sin(angle))

    def index_by_click(self, pos):
        for i in range(self.L):
            c = self.center_by_index(i)
            delta = QtGui.QVector2D(pos).distanceToPoint(QtGui.QVector2D(c))
            if delta < self.r:
                return i
        return -1

    def mousePressEvent(self, event):
        i = self.index_by_click(event.pos())
        if i >= 0:
            self.current_index = i
            self.update()

    @property
    def hour(self):
        return (self.current_index + 2) % self.L + 1

    def minumumSizeHint(self):
        return QtCore.QSize(100, 100)


def main():
    app = QtWidgets.QApplication(sys.argv)
    view = ClockWidget()
    view.resize(400, 400)
    view.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66874816

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档