English 中文(简体)
采用习俗QWidget as Popup for a QtimeEdit
原标题:Implement Custom QWidgets as Popup for a QTimeEdit

QtimeEdit没有植被,它的总体设计使得它“不能使用”。

因此,我制定了两个习俗分类:

  • class Clock_hourWidget(QWidget): class/object returns/emits first valid hour input
  • class Clock_minuteWidget(QWidget): class/object returns/emit first valid minute input

并且一将其植根于习俗的QtimeEdit:

import math
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QTimeEdit, QApplication
from PyQt5.QtCore import QTime, Qt, QTimer
from PyQt5.QtGui import QIcon


class Clock_hourWidget(QtWidgets.QWidget):
    return_hour = 15
    first = True

    def __init__(self, hours, callback, parent=None):
        super(Clock_hourWidget, self).__init__(parent)
        self.setWindowFlags(Qt.Popup)
        self.callback = callback
        self.setFixedSize(150,150)
        self.hours = hours

    def mousePressEvent(self, event):
        if self.rect().contains(event.localPos().toPoint()):
            self.first = False
            self.return_hour = self.hours
        self.close()

    @property
    def hour(self):
        if self.first:
            result = -1
        else:
            result = self.return_hour
        return result
    
    def closeEvent(self, event):
        self.callback(self.hour)
        event.accept()


class Clock_minuteWidget(QtWidgets.QWidget):
    return_minutes = 30

    def __init__(self, minutes, callback, parent=None):
        super(Clock_minuteWidget, self).__init__(parent)
        self.setWindowFlags(Qt.Popup)
        self.setFixedSize(150,150)
        self.callback = callback
        self.minutes = minutes

    def mousePressEvent(self, event):
        if self.rect().contains(event.localPos().toPoint()):
            self.return_minutes = self.minutes
        self.close()

    @property
    def minute(self):
        return self.return_minutes

    def closeEvent(self, event):
        self.callback(self.minute)
        event.accept()


class CustomTimeEdit(QTimeEdit):
    def __init__(self, parent=None):
        super(CustomTimeEdit, self).__init__(parent)
        self.setDisplayFormat("HH:mm")
        self.setCalendarPopup(False)
        # calendar_icon = QIcon.fromTheme("calendar")
        # self.setButtonSymbols(calendar_icon)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.open_clock_hourwidget()

    def return_hour(self, hour):
        self.clock_hourwidget = None
        if hour != -1:
            self.hours = hour
            self.minutes = self.time().minute()
            self.setTime(QTime(self.hours, self.minutes))
            self.clock_minutewidget = Clock_minuteWidget(self.minutes, self.clock_finalizer)
            pos = self.mapToGlobal(self.rect().bottomLeft())
            pos = self.adjust_Popup_positioning(pos, self.clock_minutewidget)
            self.clock_minutewidget.move(pos)
            self.clock_minutewidget.show()

    def open_clock_hourwidget(self):
        self.hours = self.time().hour()
        self.clock_hourwidget = Clock_hourWidget(self.hours, self.return_hour)
        pos = self.mapToGlobal(self.rect().bottomLeft())
        pos = self.adjust_Popup_positioning(pos, self.clock_hourwidget)
        self.clock_hourwidget.move(pos)
        self.clock_hourwidget.show()

    def adjust_Popup_positioning(self, pos, widget):
        screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
        screen_geometry = QApplication.desktop().screenGeometry(screen)
        if pos.y() + widget.height() > screen_geometry.height():
            pos = self.mapToGlobal(self.rect().topLeft())
            pos.setY(pos.y() - widget.height())
        if pos.x() < screen_geometry.left():
            pos.setX(screen_geometry.left())
        elif pos.x() + widget.width() > screen_geometry.right():
            pos.setX(screen_geometry.right() - widget.width())
        return pos

    def clock_finalizer(self, minute):
        self.clock_minutewidget = None
        self.setTime(QTime(self.hours, minute))


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    time_edit = CustomTimeEdit()
    time_edit.show()
    sys.exit(app.exec_())

我想,人口像QDateEdit的日历Popup(上文编码中已经具备的功能):

  • The popup should open up under the Custom QTimeEdit
  • If there is not enough space under the Custom QTimeEdit, it should show above the Custom QTimeEdit
  • If there is not enough space to the right, it should move slightly to the left and vice versa
  • Most importantly and seemingly undoable: if the user clicks outside the popup OR if the popup (maybe the whole application) looses focus (by pressing windows key or alt-tab for example): the popup closes.
  • The button of Custom QTimeEdit should look and behave like the CalendarPopup Button of QDateEdit/QDateTimeEdit (setCalendarPopup(True)): Looks should change depending on used style and platform

如果需要,我可以提供完整的法典,但出于简单的理由,我试图尽可能削减。

最佳回答

QtimeEdit and QDate Edit是基本的 du门课:在行为上,与他们继承的QDatetimeEdit相比,他们没有给出任何具体区别,事实上,它足以为获得同样结果而在QDatetimeEdit上设定一个适当的<条码>。

如今,每当<条码>、保修/编码>财产为<条码>,实际上显示,那么QDatetimeEdit做些什么? (日、月或年)。

否则,如果填满,则使用QStyle特征显示自己为一种沉积的 com箱,添加了一些>>>>。 具体来说: com船框架、其ed子领域,最重要的是arrow。

因此,为了force 在绘画时,我们需要弄清同样的行为。 请注意,由于arrow可能因国 over不同而有所不同,我们需要确保选址<代码>活性子/代码>也是一致的,而这一选择是通过强迫 mo调和核对<代码>MouseveEvent()和进入/替代活动取得的。

了解到以下法典还安装了“event <>/em>内线的内线,以便始终如一地检查 mo离开或进入:胎线为child widget,因此进入时不会给母体带来“Leave。

class CustomTimeEdit(QTimeEdit):
    clock_hourwidget = clock_minutewidget = None
    _onArrow = False
    def __init__(self, parent=None):
        super(CustomTimeEdit, self).__init__(parent)
        self.setDisplayFormat("HH:mm")
        self.setCalendarPopup(False)
        self.setMouseTracking(True)
        self.lineEdit().installEventFilter(self)

    ...
    def _setOnArrow(self, onArrow):
        if self._onArrow != onArrow:
            self._onArrow = onArrow
            self.update()

    def _checkOnArrow(self, pos):
        style = self.style()
        opt = QStyleOptionComboBox()
        opt.initFrom(self)
        arrowRect = style.subControlRect(style.CC_ComboBox, opt, 
            style.SC_ComboBoxArrow, self)
        self._setOnArrow(arrowRect.contains(pos))

    def mouseMoveEvent(self, event):
        if not event.buttons():
            self._checkOnArrow(event.pos())
        else:
            super().mouseMoveEvent(event)

    def enterEvent(self, event):
        super().enterEvent(event)
        self._checkOnArrow(self.mapFromGlobal(QCursor.pos()))

    def leaveEvent(self, event):
        super().leaveEvent(event)
        self._setOnArrow(False)

    def eventFilter(self, obj, event):
        if event.type() == event.Enter:
            self._setOnArrow(False)
        elif event.type() == event.Leave:
            self._checkOnArrow(self.mapFromGlobal(QCursor.pos()))
        return super().eventFilter(obj, event)            

    def paintEvent(self, event):
        opt = QStyleOptionSpinBox()
        QAbstractSpinBox.initStyleOption(self, opt)
        style = self.style()

        opt.subControls = (
            style.SC_ComboBoxFrame | style.SC_ComboBoxEditField
            | style.SC_ComboBoxArrow
        )
        if self.clock_hourwidget or self.clock_minutewidget:
            opt.state |= style.State_Sunken
        else:
            opt.state &= ~style.State_Sunken
        optCombo = QStyleOptionComboBox()
        optCombo.initFrom(self)
        optCombo.editable = True
        optCombo.frame = opt.frame
        optCombo.subControls = opt.subControls
        optCombo.state = opt.state

        if self._onArrow:
            optCombo.activeSubControls = style.SC_ComboBoxArrow

        qp = QPainter(self)
        self.style().drawComplexControl(
            style.CC_ComboBox, optCombo, qp, self)

请注意,QDesktopWidget已经被宣布为过时,QScreen永远应当优先。 此外,用上 desktop植被代码)来填补其职位是毫无意义的(因为它与静态<代码>QCursor.pos(”)相同,而地貌计算应当更加谨慎(而不是简单)。 例如:

...
    def open_clock_hourwidget(self):
        self.hours = self.time().hour()
        self.clock_hourwidget = Clock_hourWidget(self.hours, self.return_hour)
        self.adjustPopup(self.clock_hourwidget)

    def adjustPopup(self, widget):
        pos = self.mapToGlobal(self.rect().bottomLeft())
        geo = widget.geometry()
        geo.moveTopLeft(pos)
        screen = (QApplication.screenAt(pos) or self.screen()).geometry()
        if geo.right() > screen.right():
            geo.moveRight(screen.right())
        if geo.x() < screen.x():
            geo.moveLeft(screen.left())
        if geo.bottom() > screen.bottom():
            geo.moveBottom(screen.bottom())
        if geo.y() < screen.y():
            geo.moveTop(screen.y())
        widget.setGeometry(geo)
        widget.show()

注 虽然我们可以安全地假设,几乎没有人会使用适合人群的如此小的屏幕,但正式考虑这种可能性仍然更好。

最后,一些进一步的建议:

  • it makes little point to have two instance attributes for the hour/minute widgets, since they are always being shown separately and eventually destroyed (and then recreated again); a better approach would be to create them only when necessary, and eventually reuse them, possibly updating their value whenever the timeChanged signal of QTimeEdit is emitted;
  • while there s technically nothing wrong in using "callbacks", the signal/slot mechanism of Qt provides better modularity and separation of concerns; interestingly enough, you re using the term "emits" in your question, but you re not emitting anything; instead of using callbacks, use a custom signal and actually emit it when required;
  • QAbstractSpinBox based classes handle mouse events that are not handled by their inner lineEdit(), meaning that simply overriding mousePressEvent() based on the button is probably inconsistent, as the user may be clicking on the frame; consider checking the actual press position based on how the _checkOnArrow() function above was implemented, which is what editable combo boxes actually do;
问题回答

暂无回答




相关问题
Can Django models use MySQL functions?

Is there a way to force Django models to pass a field to a MySQL function every time the model data is read or loaded? To clarify what I mean in SQL, I want the Django model to produce something like ...

An enterprise scheduler for python (like quartz)

I am looking for an enterprise tasks scheduler for python, like quartz is for Java. Requirements: Persistent: if the process restarts or the machine restarts, then all the jobs must stay there and ...

How to remove unique, then duplicate dictionaries in a list?

Given the following list that contains some duplicate and some unique dictionaries, what is the best method to remove unique dictionaries first, then reduce the duplicate dictionaries to single ...

What is suggested seed value to use with random.seed()?

Simple enough question: I m using python random module to generate random integers. I want to know what is the suggested value to use with the random.seed() function? Currently I am letting this ...

How can I make the PyDev editor selectively ignore errors?

I m using PyDev under Eclipse to write some Jython code. I ve got numerous instances where I need to do something like this: import com.work.project.component.client.Interface.ISubInterface as ...

How do I profile `paster serve` s startup time?

Python s paster serve app.ini is taking longer than I would like to be ready for the first request. I know how to profile requests with middleware, but how do I profile the initialization time? I ...

Pragmatically adding give-aways/freebies to an online store

Our business currently has an online store and recently we ve been offering free specials to our customers. Right now, we simply display the special and give the buyer a notice stating we will add the ...

Converting Dictionary to List? [duplicate]

I m trying to convert a Python dictionary into a Python list, in order to perform some calculations. #My dictionary dict = {} dict[ Capital ]="London" dict[ Food ]="Fish&Chips" dict[ 2012 ]="...

热门标签