QPainterPath 沿多条路径绘制和放置项目 [英] QPainterPath drawing and placering items along multiple paths

查看:76
本文介绍了QPainterPath 沿多条路径绘制和放置项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从以前的代码行中开发并添加了一些特殊行为.想法是在矩形和圆形内部的某个路径上绘制小圆形,并根据预先给定的距离放置它们.我想根据用户给定的距离以编程方式自动绘制.IE.距离和数量会有所不同.

I have from previous codelines developed and added some special behaviour. The idea is to draw small circle shapes on a certain path inside rectangular shape and circular shape and place them according to the distance given in advance. I want to draw automatically and programmatically based on given distance by user. ie. distance and number will vary.

我自己看了这个练习,并用非常复杂的 for 循环和长代码行来解决这个问题.我害怕在这里重复同样的过程.

I have looked myself at this exercise, and reached to solve this with very complicated for-loops and long codelines. I am afraid of repeating same procedure here.

我想要达到的目标:

在矩形的情况下,圆的大小由下式给出:

In case of rectangular shape, size of circle is given by:

self._rebar = QtCore.QSizeF(yb, yb)

每个圆从顶部到圆心的垂直距离由下式给出:

and vertical distance of each circle measured from top to circle center is given by:

cb, ct, ys, yb, yt = 25, 25, 8, 12, 12
di, dis = h-cb-ys-yb/2, ct+ys+yt/2
self.db, self.dt = [di, di, di, di-25, di-25, di-25], [dis, dis, dis, dis+25, dis+25]

认为下一个输入将增加或减少推理更少或更多距离值的钢筋数量.我将能够有规律地、有规律地绘制和绘制它们.

Think that next input would increase or decrease number of rebars reasoning less or more distance values. That I would be able to plot and draw them orderly and regularly.

输入为圆形.

cb, ys, yb = 25, 8, 12
self.As = [yb, yb, yb, yb, yb, yb, yb]
self._rebar = QtCore.QSizeF(yb, yb)

在圆形的情况下,有没有办法绘制并遵循圆形路径并放置带有弧角的圆形?我的意思是,我认为最简单的方法是测量第一个钢筋数量并有一个距离,然后用弧角和长度将它们按顺序放置和组织,无论圆形项目的数量是多少!

In case of circle is there a way to draw and follow a circular path and place circles with arc angles? What I mean by that, I think the easist way to measure first number of rebars and have a distance then with a arc angle and length would be able to place and organise them in order, no matter what the number of circles items would become!

我很欣赏能解决这个问题并避免冗长编码的任何简单而聪明的方法.

I appreciate any easy and smart way to come around that and to avoid long coding.

解释矩形:

根据您的信息,就是这样,list self.db = [di, di, di, di+25, di+25, di+25] 将传递给来自另一个的函数类,已经计算了一层中有多少个圆圈,即.例如,self.db = [200, 200, 180, 180] 表示一层只有 2 个圆,另一层只有 2 个圆.所以 beff 在第一层只会包含 2 个圆圈.在这种情况下,需要找出如何将它们按顺序排列并定期放置.

To your information, it is that so, list self.db = [di, di, di, di+25, di+25, di+25] would pass to function coming from another class, whic already has calculated how many circles would be in one layer, ie. for exampel, self.db = [200, 200, 180, 180] means that there would only be 2 circles at one layer and 2 in other layer. So beff would only contain 2 circles in first layer. in that case needs to find out how to place them in order and regularly.

我更正了这行代码 self.db, self.dt = [di, di, di, di-25, di-25, di-25], [dis, dis, dis, dis+25, dis+25]

代码:

from PyQt5 import QtCore, QtGui, QtWidgets

class Foo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setGeometry(QtCore.QRect(200, 100, 800, 800))

        self.button = Button()
        self.paint = Createpaintwidget()
        self.button.valuesChanged.connect(self.paint.set_size_squares)
        self.button.valueChanged.connect(self.paint.set_size_round)

        self.lay = QtWidgets.QVBoxLayout(self)
        self.lay.addWidget(self.paint)
        self.lay.addWidget(self.button)

class Createpaintwidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.sizeHint()        
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)

        self._size = QtCore.QSizeF()
        self._path = QtGui.QPainterPath()
        self._rect = QtCore.QRectF()
        self._type = QtGui.QRegion.Rectangle
        self._factor = 1.0

        self._sizeouter = QtCore.QSizeF()
        self._rectouter = QtCore.QRectF()
        self._sizeinner = QtCore.QSizeF()
        self._rectinner = QtCore.QRectF()
        self._rebar = QtCore.QSizeF()
        self._rectrebar = QtCore.QRectF()

        self._pos = QtCore.QPointF()
        self._initial_flag = False
        fnt = self.font() 
        fnt.setPointSize(20) 
        self.setFont(fnt) 

    def showEvent(self, event):
        if not self._initial_flag:
            self._pos = self.rect().center()
            self._initial_flag = True

    @QtCore.pyqtSlot(int, int)
    def set_size_squares(self, w, h):
        cb, ct, ys, yb, yt = 25, 25, 8, 12, 12
        di, dis, ccb, cct = h-cb-ys-yb/2, ct+ys+yt/2, cb+ys+0.5*yb, ct+ys+0.5*yt
        self.db, self.dt = [di, di, di, di-25, di-25, di-25], [dis, dis, dis, dis+25, dis+25]
        beff_b, beff_t = w - 2*ccb, w - 2*cct
        db_d = dict(set((x,self.db.count(x)) for x in filter(lambda rec : self.db.count(rec)>1,self.db)))
        db_l = [k for k in dict.values(db_d)]
        dt_d = dict(set((x,self.dt.count(x)) for x in filter(lambda rec : self.dt.count(rec)>1,self.dt)))
        dt_l = [k for k in dict.values(dt_d)]
        xb, xt = [beff_b/(k-1) for k in db_l], [beff_t/(k-1) for k in dt_l]
        self.dbx, self.dtx = [], []
        start = 0 
        for k, b in enumerate(db_l): 
            sub_A = self.db[start:start+b] 
            start = start+b 
            for i, _ in enumerate(sub_A): 
                self.dbx.append(ccb+i*xb[k])
        start = 0 
        for k, b in enumerate(dt_l): 
            sub_A = self.dt[start:start+b] 
            start = start+b 
            for i, _ in enumerate(sub_A): 
                self.dtx.append(cct+i*xt[k])


        self._path = QtGui.QPainterPath()
        self._size = QtCore.QSizeF(w, h)
        self._sizeouter = QtCore.QSizeF(w-cb, h-ct)
        self._sizeinner = QtCore.QSizeF(w-cb-ys, h-ct-ys)
        self._rebar = QtCore.QSizeF(yb, yb)
        self._type = QtGui.QRegion.Rectangle
        self.updatePath()


    @QtCore.pyqtSlot(int)
    def set_size_round(self, v):
        cb, ys, yb = 25, 8, 12
        self.As = [yb, yb, yb, yb, yb, yb, yb]
        self._path = QtGui.QPainterPath()
        self._size = QtCore.QSizeF(v, v)
        self._sizeouter = QtCore.QSizeF(v-cb, v-cb)
        self._sizeinner = QtCore.QSizeF(v-cb-ys, v-cb-ys)
        self._rebar = QtCore.QSizeF(yb, yb)
        self._type = QtGui.QRegion.Ellipse
        self.updatePath()

    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush(QtCore.Qt.black)
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(pen)
        painter.setBrush(brush)

        painter.translate(self.rect().center())
        painter.scale(self._factor, self._factor)
        painter.translate(-self.rect().center())

        painter.translate(self._pos)
        painter.drawPath(self._path)



        S = (self._rectouter.size() + self._rectinner.size())/2
        s = (self._rectouter.size() - self._rectinner.size())/2
        r = QtCore.QRectF(QtCore.QPointF(), S)
        r.moveCenter(self._rectouter.center())
        path = QtGui.QPainterPath()
        painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))

        if self._type == QtGui.QRegion.Rectangle:
            painter.drawRect(self._rect)
            path.addRoundedRect(r, 10, 10)

            painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
            painter.drawEllipse(self._rectrebar)

        elif self._type == QtGui.QRegion.Ellipse:
            painter.drawEllipse(self._rect)
            path.addEllipse(r)

            painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
            painter.drawEllipse(self._rectrebar)

        stroker = QtGui.QPainterPathStroker()
        stroker.setWidth(s.width())
        stroke_path = stroker.createStroke(path)
        painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
        painter.drawPath(stroke_path)

    def mousePressEvent(self, event):
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self._initial_pos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        delta = event.pos() - self._initial_pos
        self._path.translate(delta)
        self._rect.translate(delta)
        self._rectinner.translate(delta)
        self._rectouter.translate(delta)
        self._rectrebar.translate(delta)
        self.update()
        self._initial_pos = event.pos()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        QtWidgets.QApplication.restoreOverrideCursor()
        super().mouseReleaseEvent(event)

    def updatePath(self):
        r = QtCore.QRectF(QtCore.QPointF(), self._size)
        ro = QtCore.QRectF(QtCore.QPointF(), self._sizeouter)
        ri = QtCore.QRectF(QtCore.QPointF(), self._sizeinner)
        rr = QtCore.QRectF(QtCore.QPointF(), self._rebar)
        r.moveCenter(QtCore.QPointF())
        ro.moveCenter(QtCore.QPointF())
        ri.moveCenter(QtCore.QPointF())
        rr.moveCenter(QtCore.QPointF())

        self._rectouter = QtCore.QRectF(ro)
        self._rectinner = QtCore.QRectF(ri)
        self._rect = QtCore.QRectF(r)
        self._rectrebar = QtCore.QRectF(rr)     
        self.update()

    def wheelEvent(self, event):
        self._factor *= 1.01**(event.angleDelta().y()/15.0)
        self.update()
        super().wheelEvent(event)

class Button(QtWidgets.QWidget):
    valueChanged = QtCore.pyqtSignal(int)
    valuesChanged = QtCore.pyqtSignal(int,int)
    def __init__(self, parent=None):
        super(Button, self).__init__(parent)
        roundbutton = QtWidgets.QPushButton('Round')
        squarebutton = QtWidgets.QPushButton('Square')
        Alay = QtWidgets.QVBoxLayout(self)
        Alay.addWidget(roundbutton)
        Alay.addWidget(squarebutton)
        self.value = QtWidgets.QLabel()
        roundbutton.clicked.connect(self.getbuttonfunc)
        squarebutton.clicked.connect(self.sqaurebuttonfunc)

    @QtCore.pyqtSlot()
    def getbuttonfunc(self):
        number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
                                         self.tr("Input:"), 1, 1)
        if ok:
            self.valueChanged.emit(number)

    @QtCore.pyqtSlot()
    def sqaurebuttonfunc(self):
        number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
                                         self.tr("Input:"), 1, 1)
        if ok:
            self.valuesChanged.emit(number, number)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Foo()
    w.show()
    sys.exit(app.exec_())

更新代码:

@QtCore.pyqtSlot(int, int)
def set_size_squares(self, w, h):
    cb, ct, ys, yb, yt = 25, 25, 8, 12, 12
    di, dis, ccb, cct = h-cb-ys-yb/2, ct+ys+yt/2, cb+ys+0.5*yb, ct+ys+0.5*yt
    self.db, self.dt = [di, di, di, di-25, di-25, di-25], [dis, dis, dis, dis+25, dis+25]
    beff_b, beff_t = w - 2*ccb, w - 2*cct
    db_d = dict(set((x,self.db.count(x)) for x in filter(lambda rec : self.db.count(rec)>1,self.db)))
    db_l = [k for k in dict.values(db_d)]
    dt_d = dict(set((x,self.dt.count(x)) for x in filter(lambda rec : self.dt.count(rec)>1,self.dt)))
    dt_l = [k for k in dict.values(dt_d)]
    xb, xt = [beff_b/(k-1) for k in db_l], [beff_t/(k-1) for k in dt_l]
    self.dbx, self.dtx = [], []
    start = 0 
    for k, b in enumerate(db_l): 
        sub_A = self.db[start:start+b] 
        start = start+b 
        for i, _ in enumerate(sub_A): 
            self.dbx.append(ccb+i*xb[k])
    start = 0 
    for k, b in enumerate(dt_l): 
        sub_A = self.dt[start:start+b] 
        start = start+b 
        for i, _ in enumerate(sub_A): 
            self.dtx.append(cct+i*xt[k])

                  ----------

底部圆的 y 坐标和列表中给出的相应 x 坐标:

The y coordinates for bottom circles and corresponding x coordinates given in list:

self.dbx = [x2,x3,x1,x5,x6,x4]
self.db = [y2, y3, y1, y5, y6, y4]

顶部圆的 y 坐标和列表中给出的相应 x 坐标:

The y coordinates for top circles and corresponding x coordinates given in list:

self.dtx = [x2,x3,x1,x5,x4]
self.dt = [y2, y3, y1, y5, y4]

推荐答案

考虑到您提供的位置列表以及关于矩形左上角的位置列表,结果如下:

Considering the lists of positions that you have provided and that are regarding the topleft of the rectangle the result is the following:

import math
from PyQt5 import QtCore, QtGui, QtWidgets

class Foo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Foo, self).__init__(parent)
        self.setGeometry(QtCore.QRect(200, 100, 800, 800))

        self.button = Button()
        self.paint = Createpaintwidget()
        self.button.valuesChanged.connect(self.paint.set_size_squares)
        self.button.valueChanged.connect(self.paint.set_size_round)

        self.lay = QtWidgets.QVBoxLayout(self)
        self.lay.addWidget(self.paint)
        self.lay.addWidget(self.button)

class Createpaintwidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.sizeHint()        
        self.setBackgroundRole(QtGui.QPalette.Base)     
        self.setAutoFillBackground(True)

        self._size = QtCore.QSizeF()
        self._path = QtGui.QPainterPath()
        self._rect = QtCore.QRectF()
        self._type  = None
        self._factor = 1.0

        self._sizeouter = QtCore.QSizeF()
        self._rectouter = QtCore.QRectF()
        self._sizeinner = QtCore.QSizeF()
        self._rectinner = QtCore.QRectF()
        self._rebar = QtCore.QSizeF()
        self._rectrebar = QtCore.QRectF()

        self._pos = QtCore.QPointF()
        self._initial_flag = False
        fnt = self.font() 
        fnt.setPointSize(20) 
        self.setFont(fnt) 

    def showEvent(self, event):
        if not self._initial_flag:
            self._pos = self.rect().center()
            self._initial_flag = True

    @QtCore.pyqtSlot(int, int)
    def set_size_squares(self, w, h):
        cb, ct, ys, yb, yt = 25, 25, 8, 12, 12
        di, dis, ccb, cct = h-cb-ys-yb/2, ct+ys+yt/2, cb+ys+0.5*yb, ct+ys+0.5*yt
        self.db, self.dt = [di, di, di, di-25, di-25, di-25], [dis, dis, dis, dis+25, dis+25]
        beff_b, beff_t = w - 2*ccb, w - 2*cct
        db_d = dict(set((x,self.db.count(x)) for x in filter(lambda rec : self.db.count(rec)>1,self.db)))
        db_l = [k for k in dict.values(db_d)]
        dt_d = dict(set((x,self.dt.count(x)) for x in filter(lambda rec : self.dt.count(rec)>1,self.dt)))
        dt_l = [k for k in dict.values(dt_d)]
        xb, xt = [beff_b/(k-1) for k in db_l], [beff_t/(k-1) for k in dt_l]
        self.dbx, self.dtx = [], []
        start = 0 
        for k, b in enumerate(db_l): 
            sub_A = self.db[start:start+b] 
            start = start+b 
            for i, _ in enumerate(sub_A): 
                self.dbx.append(ccb+i*xb[k])
        start = 0 
        for k, b in enumerate(dt_l): 
            sub_A = self.dt[start:start+b] 
            start = start+b 
            for i, _ in enumerate(sub_A): 
                self.dtx.append(cct+i*xt[k])

        self._path = QtGui.QPainterPath()
        self._size = QtCore.QSizeF(w, h)
        self._sizeouter = QtCore.QSizeF(w-cb, h-ct)
        self._sizeinner = QtCore.QSizeF(w-cb-ys, h-ct-ys)
        self._rebar = QtCore.QSizeF(yb, yb)
        self._type = QtGui.QRegion.Rectangle
        self.updatePath()


    @QtCore.pyqtSlot(int)
    def set_size_round(self, v):
        cb, ys, yb = 25, 8, 12
        self.As = [yb, yb, yb, yb, yb, yb, yb]
        self._path = QtGui.QPainterPath()
        self._size = QtCore.QSizeF(v, v)
        self._sizeouter = QtCore.QSizeF(v-cb, v-cb)
        self._sizeinner = QtCore.QSizeF(v-cb-ys, v-cb-ys)
        self._rebar = QtCore.QSizeF(yb, yb)
        self._type = QtGui.QRegion.Ellipse
        self.updatePath()

    def paintEvent(self, event):
        pen = QtGui.QPen()
        brush = QtGui.QBrush(QtCore.Qt.black)
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(pen)
        painter.setBrush(brush)

        painter.translate(self.rect().center())
        painter.scale(self._factor, self._factor)
        painter.translate(-self.rect().center())

        painter.translate(self._pos)
        painter.drawPath(self._path)



        S = (self._rectouter.size() + self._rectinner.size())/2
        s = (self._rectouter.size() - self._rectinner.size())/2
        r = QtCore.QRectF(QtCore.QPointF(), S)
        r.moveCenter(self._rectouter.center())
        path = QtGui.QPainterPath()
        painter.setBrush(QtGui.QBrush(QtCore.Qt.gray, QtCore.Qt.Dense7Pattern))

        if self._type == QtGui.QRegion.Rectangle:
            painter.drawRect(self._rect)
            path.addRoundedRect(r, 10, 10)
            _r = QtCore.QRectF(self._rectrebar)
            for x, y in zip(self.dtx + self.dbx , self.dt + self.db):
                _r.moveCenter(QtCore.QPointF(x, y) + self._rect.topLeft())
                painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
                painter.drawEllipse(_r)

        elif self._type == QtGui.QRegion.Ellipse:
            painter.drawEllipse(self._rect)
            path.addEllipse(r)
            painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
            n = 7
            for i, _ in enumerate(self.As):
                angle = (i*2.0/n + 1)*math.pi
                radius = .5 *(r.width() - s.width() - self._rectrebar.width())
                dx = radius*QtCore.QPointF(math.cos(angle), math.sin(angle))
                r_ = QtCore.QRectF(self._rectrebar)
                r_.translate(dx)
                painter.drawEllipse(r_)

        stroker = QtGui.QPainterPathStroker()
        stroker.setWidth(s.width())
        stroke_path = stroker.createStroke(path)
        painter.setBrush(QtGui.QBrush(QtCore.Qt.cyan, QtCore.Qt.SolidPattern))
        painter.drawPath(stroke_path)

    def mousePressEvent(self, event):
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self._initial_pos = event.pos()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        delta = event.pos() - self._initial_pos
        self._path.translate(delta)
        self._rect.translate(delta)
        self._rectinner.translate(delta)
        self._rectouter.translate(delta)
        self._rectrebar.translate(delta)
        self.update()
        self._initial_pos = event.pos()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        QtWidgets.QApplication.restoreOverrideCursor()
        super().mouseReleaseEvent(event)

    def updatePath(self):
        r = QtCore.QRectF(QtCore.QPointF(), self._size)
        ro = QtCore.QRectF(QtCore.QPointF(), self._sizeouter)
        ri = QtCore.QRectF(QtCore.QPointF(), self._sizeinner)
        rr = QtCore.QRectF(QtCore.QPointF(), self._rebar)
        r.moveCenter(QtCore.QPointF())
        ro.moveCenter(QtCore.QPointF())
        ri.moveCenter(QtCore.QPointF())
        rr.moveCenter(QtCore.QPointF())

        self._rectouter = QtCore.QRectF(ro)
        self._rectinner = QtCore.QRectF(ri)
        self._rect = QtCore.QRectF(r)
        self._rectrebar = QtCore.QRectF(rr)     
        self.update()

    def wheelEvent(self, event):
        self._factor *= 1.01**(event.angleDelta().y()/15.0)
        self.update()
        super().wheelEvent(event)

class Button(QtWidgets.QWidget):
    valueChanged = QtCore.pyqtSignal(int)
    valuesChanged = QtCore.pyqtSignal(int,int)
    def __init__(self, parent=None):
        super(Button, self).__init__(parent)
        roundbutton = QtWidgets.QPushButton('Round')
        squarebutton = QtWidgets.QPushButton('Square')
        Alay = QtWidgets.QVBoxLayout(self)
        Alay.addWidget(roundbutton)
        Alay.addWidget(squarebutton)
        self.value = QtWidgets.QLabel()
        roundbutton.clicked.connect(self.getbuttonfunc)
        squarebutton.clicked.connect(self.sqaurebuttonfunc)

    @QtCore.pyqtSlot()
    def getbuttonfunc(self):
        number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
                                         self.tr("Input:"), 1, 1)
        if ok:
            self.valueChanged.emit(number)

    @QtCore.pyqtSlot()
    def sqaurebuttonfunc(self):
        number, ok = QtWidgets.QInputDialog.getInt(self, self.tr("Set Number"),
                                         self.tr("Input:"), 1, 1)
        if ok:
            self.valuesChanged.emit(number, number)


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Foo()
    w.show()
    sys.exit(app.exec_())

这篇关于QPainterPath 沿多条路径绘制和放置项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆