Currently, 2 layouts have been added, and buttons are added using FlowLayout at the top and below the label.
on Qrubberband
When the mouse is don't move, there is nothing wrong. When I drag, there is a problem with the selection.
Where did the problem come from?
# -*- coding: utf-8 -*-
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
class QToolButton(QToolButton):
def __init__(self, label='', icon='', icon_size=100):
super(QToolButton, self).__init__()
self.label = label
self.icon = icon
self.icon_size = icon_size
self.create()
def create(self):
self.setText(self.label)
self.setIcon(QIcon(self.icon))
self.setIconSize(QSize(self.icon_size, self.icon_size))
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.setCheckable(True)
self.setFocusPolicy(Qt.NoFocus)
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.ui()
self.add_thumbnail()
def ui(self):
self.setContentsMargins(2, 2, 2, 2)
self.resize(1000, 800)
self.centralwidget = QWidget(self)
self.gridLayout = QGridLayout(self.centralwidget)
self.label = QLabel(self.centralwidget)
self.label.setText('LABEL IMAGE')
self.label.setMinimumSize(QSize(0, 100))
self.label.setMaximumSize(QSize(16777215, 100))
self.label.setAlignment(Qt.AlignCenter)
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
self.frame = QFrame(self.centralwidget)
self.frame.setFrameShape(QFrame.StyledPanel)
self.frame.setFrameShadow(QFrame.Raised)
self.gridLayout.addWidget(self.frame, 1, 0, 1, 1)
self.setCentralWidget(self.centralwidget)
self.flow_layout = FlowLayout()
self.frame.setLayout(self.flow_layout)
def add_thumbnail(self):
for i in range(10):
button = QToolButton(label='test', icon='test')
self.flow_layout.addWidget(button)
button.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == event.MouseButtonPress:
self.origin = source.mapTo(self, event.pos())
elif event.type() == event.MouseMove and event.buttons():
if not self.rubberBand.isVisible():
distance = (source.mapTo(self, event.pos()) - self.origin).manhattanLength()
if distance > QApplication.startDragDistance():
if isinstance(source, QAbstractButton) and source.isDown():
source.setDown(False)
self.rubberBand.show()
if self.rubberBand.isVisible():
self.resizeRubberBand(source.mapTo(self, event.pos()))
event.accept()
return True
elif event.type() == event.MouseButtonRelease and self.rubberBand.isVisible():
self.closeRubberBand()
event.accept()
return True
return super(Window, self).eventFilter(source, event)
def startRubberBand(self, pos):
self.origin = pos
self.rubberBand.setGeometry(
QRect(self.origin, QSize()))
self.rubberBand.show()
def resizeRubberBand(self, pos):
if self.rubberBand.isVisible():
self.rubberBand.setGeometry(
QRect(self.origin, pos).normalized())
def closeRubberBand(self):
if self.rubberBand.isVisible():
self.rubberBand.hide()
selected = []
rect = self.rubberBand.geometry()
for child in self.findChildren(QToolButton):
if rect.intersects(child.geometry()):
selected.append(child)
if selected:
for i in selected:
if i.isChecked():
i.setChecked(False)
else:
i.setChecked(True)
def mousePressEvent(self, event):
self.startRubberBand(event.pos())
QMainWindow.mousePressEvent(self, event)
def mouseMoveEvent(self, event):
self.resizeRubberBand(event.pos())
QMainWindow.mouseMoveEvent(self, event)
def mouseReleaseEvent(self, event):
self.closeRubberBand()
QMainWindow.mouseReleaseEvent(self, event)
class FlowLayout(QLayout):
def __init__(self, parent=None, margin=0, spacing=-1):
super(FlowLayout, self).__init__(parent)
if parent is not None:
self.setContentsMargins(margin, margin, margin, margin)
self.setSpacing(spacing)
self.margin = margin
# spaces between each item
self.spaceX = 2
self.spaceY = 2
self.itemList = []
def __del__(self):
item = self.takeAt(0)
while item:
item = self.takeAt(0)
def addItem(self, item):
self.itemList.append(item)
def count(self):
return len(self.itemList)
def itemAt(self, index):
if index >= 0 and index < len(self.itemList):
return self.itemList[index]
return None
def takeAt(self, index):
if index >= 0 and index < len(self.itemList):
return self.itemList.pop(index)
return None
def expandingDirections(self):
return Qt.Orientations(Qt.Orientation(0))
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
height = self.doLayout(QRect(0, 0, width, 0), True)
return height
def setGeometry(self, rect):
super(FlowLayout, self).setGeometry(rect)
self.doLayout(rect, False)
def sizeHint(self):
return self.minimumSize()
def minimumSize(self):
size = QSize()
for item in self.itemList:
size = size.expandedTo(item.minimumSize())
size += QSize(2 * self.margin, 2 * self.margin)
return size
def doLayout(self, rect, testOnly):
x = rect.x()
y = rect.y()
lineHeight = 0
for item in self.itemList:
wid = item.widget()
nextX = x + item.sizeHint().width() + self.spaceX
if nextX - self.spaceX > rect.right() and lineHeight > 0:
x = rect.x()
y = y + lineHeight + self.spaceY
nextX = x + item.sizeHint().width() + self.spaceX
lineHeight = 0
if not testOnly:
item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))
x = nextX
lineHeight = max(lineHeight, item.sizeHint().height())
return y + lineHeight - rect.y()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Where did the problem come from?
When left-clicking the mouse, when it was mousePressEvent, it worked normally, and when it was mouseReleaseEvent, there was a problem with widget selection.
I have solved it now
the code is
def closeRubberBand(self):
if self.rubberBand.isVisible():
self.rubberBand.hide()
selected = []
rect = self.rubberBand.geometry()
for child in self.findChildren(QToolButton):
if self.flow_layout_widget:
layout_x = self.flow_layout_widget.pos().toTuple()[0]
layout_y = self.flow_layout_widget.pos().toTuple()[1]
child_w = child.geometry().size().toTuple()[0]
child_h = child.geometry().size().toTuple()[1]
cx, cy, cw, ch = (child.geometry().x() + layout_x, child.geometry().y() + layout_y, child_w, child_h)
child_rect = QRect(cx, cy, cw, ch)
else:
child_rect = child.geometry()
if rect.intersects(child_rect):
selected.append(child)
if selected:
for i in selected:
if i.isChecked():
i.setChecked(False)
else:
i.setChecked(True)
Could there be an easier way?
Related
Regarding great answer by #ekhumoro and modification by #Oak_3260548 and #eyllanesc, I would like to first have a normal view without filters below the QHeaderView and a button to activate (show) the filters:
After I click the Filter button (which has property button.setCheckable(True)) I want to get:
Notice: the Filter button is pressed. Now I want the row with filters below the QHeaderView to disappear when I click again to Filter button (unpress it):
How can I do that, in other words how to get QHeaderView of the QTableView to be the old plain one? It would be very convenient to have that kind of feature in the app that user can toggle filter row below QHeaderView with button (or any other way) whenever he wants. The behavior of the app would be to cancel all filters in view when button is unpressed and filter row disappears, but that is not important for this question.
Edit:
Here is MRE:
import sys
from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import (
QHeaderView,
QWidget,
QLineEdit,
QApplication,
QTableView,
QVBoxLayout,
QHBoxLayout,
QComboBox,
QPushButton,
QCheckBox,
QMessageBox,
)
class Widget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.btn = QPushButton()
self.btn.setText("=")
self.btn.setFixedWidth(20)
self.linee = QLineEdit()
self.linee.setPlaceholderText("Filter")
lay = QHBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.setSpacing(0)
lay.addWidget(self.btn)
lay.addWidget(self.linee)
class FilterHeader(QHeaderView):
filterActivated = pyqtSignal()
def __init__(self, parent):
super().__init__(Qt.Horizontal, parent)
self._editors = []
self._padding = 4
self.setStretchLastSection(True)
# self.setResizeMode(QHeaderView.Stretch)
self.setDefaultAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.setSortIndicatorShown(False)
self.sectionResized.connect(self.adjustPositions)
parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)
def setFilterBoxes(self, count):
while self._editors:
editor = self._editors.pop()
editor.deleteLater()
for index in range(count):
editor = self.create_editor(self.parent(), index)
self._editors.append(editor)
self.adjustPositions()
def create_editor(self, parent, index):
if index == 1: # Empty
editor = QWidget()
elif index == 2: # Number filter (>|=|<)
editor = Widget(parent)
editor.linee.returnPressed.connect(self.filterActivated)
editor.btn.clicked.connect(self.changebuttonsymbol)
elif index == 3:
editor = QComboBox(parent)
editor.addItems(["", "Combo", "One", "Two", "Three"])
editor.currentIndexChanged.connect(self.filterActivated)
elif index == 4:
editor = QPushButton(parent)
editor.clicked.connect(self.filterActivated)
editor.setText("Button")
elif index == 5:
editor = QCheckBox(parent)
editor.clicked.connect(self.filterActivated)
editor.setTristate(True)
editor.setCheckState(Qt.Checked)
editor.setText("CheckBox")
else:
editor = QLineEdit(parent)
editor.setPlaceholderText("Filter")
editor.returnPressed.connect(self.filterActivated)
return editor
def sizeHint(self):
size = super().sizeHint()
if self._editors:
height = self._editors[0].sizeHint().height()
size.setHeight(size.height() + height + self._padding)
return size
def updateGeometries(self):
if self._editors:
height = self._editors[0].sizeHint().height()
self.setViewportMargins(0, 0, 0, height + self._padding)
else:
self.setViewportMargins(0, 0, 0, 0)
super().updateGeometries()
self.adjustPositions()
def adjustPositions(self):
for index, editor in enumerate(self._editors):
if not isinstance(editor, QWidget):
continue
height = editor.sizeHint().height()
compensate_y = 0
compensate_x = 0
if type(editor) is QComboBox:
compensate_y = +2
elif type(editor) in (QWidget, Widget):
compensate_y = -1
elif type(editor) is QPushButton:
compensate_y = -1
elif type(editor) is QCheckBox:
compensate_y = 4
compensate_x = 4
editor.move(
self.sectionPosition(index) - self.offset() + 1 + compensate_x,
height + (self._padding // 2) + 2 + compensate_y,
)
editor.resize(self.sectionSize(index), height)
def filterText(self, index):
for editor in self._editors:
if hasattr(editor, "text") and callable(editor.text):
return editor.text()
return ""
def setFilterText(self, index, text):
for editor in self._editors:
if hasattr(editor, "setText") and callable(editor.setText):
editor.setText(text)
def clearFilters(self):
for editor in self._editors:
editor.clear()
def changebuttonsymbol(self):
nbtn = self.sender()
if nbtn.text() == "=":
nbtn.setText(">")
elif nbtn.text() == ">":
nbtn.setText("<")
else:
nbtn.setText("=")
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.filter_button = QPushButton("Filter")
self.filter_button.setCheckable(True)
self.filter_button.setChecked(True)
self.filter_button.clicked.connect(self.on_button_clicked)
self.view = QTableView()
self.view.horizontalHeader().setStretchLastSection(True)
button_layout = QHBoxLayout()
button_layout.addStretch()
button_layout.addWidget(self.filter_button)
layout = QVBoxLayout(self)
layout.addLayout(button_layout)
layout.addWidget(self.view)
header = FilterHeader(self.view)
self.view.setHorizontalHeader(header)
self.view.verticalHeader().setVisible(False)
#model = QStandardItemModel(self.view)
model = QStandardItemModel(5, 7, self.view)
for i in range(5):
for j in range(7):
item = QStandardItem(str(i+j))
model.setItem(i, j, item)
model.setHorizontalHeaderLabels("One Two Three Four Five Six Seven".split())
self.view.setModel(model)
header.setFilterBoxes(model.columnCount())
header.filterActivated.connect(self.handleFilterActivated)
def handleFilterActivated(self):
header = self.view.horizontalHeader()
print()
for index in range(header.count()):
if index != 4:
print(index, header.filterText(index))
else:
print("Button")
def on_button_clicked(self):
if self.filter_button.isChecked():
QMessageBox.information(None, "", "Now I want the row with filters below the QHeaderView to appear again.")
else:
QMessageBox.information(None, "", "Now I want the row with filters below the QHeaderView to disappear.")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
#window.setGeometry(800, 100, 600, 300)
window.resize(950, 220)
window.show()
sys.exit(app.exec_())
EDIT: The accepted solution works fine in many cases but in some cases it freezes the GUI when the window is closed, so I found a workaround:
def closeEvent(self, event):
self.view.horizontalHeader().visible_editors = False
You have to create a property that handles the visibility of the editors:
class FilterHeader(QHeaderView):
filterActivated = pyqtSignal()
def __init__(self, parent):
super().__init__(Qt.Horizontal, parent)
self._visible_editors = False
self._editors = []
self._padding = 4
self.setStretchLastSection(True)
# self.setResizeMode(QHeaderView.Stretch)
self.setDefaultAlignment(Qt.AlignLeft | Qt.AlignVCenter)
self.setSortIndicatorShown(False)
self.sectionResized.connect(self.adjustPositions)
parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)
#property
def visible_editors(self):
return self._visible_editors
#visible_editors.setter
def visible_editors(self, is_visible):
self._visible_editors = is_visible
for editor in self._editors:
editor.setVisible(self.visible_editors)
self.updateGeometries()
def setFilterBoxes(self, count):
while self._editors:
editor = self._editors.pop()
editor.deleteLater()
for index in range(count):
editor = self.create_editor(self.parent(), index)
editor.setVisible(self.visible_editors)
self._editors.append(editor)
self.adjustPositions()
def create_editor(self, parent, index):
if index == 1: # Empty
editor = QWidget()
elif index == 2: # Number filter (>|=|<)
editor = Widget(parent)
editor.linee.returnPressed.connect(self.filterActivated)
editor.btn.clicked.connect(self.changebuttonsymbol)
elif index == 3:
editor = QComboBox(parent)
editor.addItems(["", "Combo", "One", "Two", "Three"])
editor.currentIndexChanged.connect(self.filterActivated)
elif index == 4:
editor = QPushButton(parent)
editor.clicked.connect(self.filterActivated)
editor.setText("Button")
elif index == 5:
editor = QCheckBox(parent)
editor.clicked.connect(self.filterActivated)
editor.setTristate(True)
editor.setCheckState(Qt.Checked)
editor.setText("CheckBox")
else:
editor = QLineEdit(parent)
editor.setPlaceholderText("Filter")
editor.returnPressed.connect(self.filterActivated)
return editor
def sizeHint(self):
size = super().sizeHint()
if self._editors and self.visible_editors:
height = self._editors[0].sizeHint().height()
size.setHeight(size.height() + height + self._padding)
return size
def updateGeometries(self):
if self._editors and self.visible_editors:
height = self._editors[0].sizeHint().height()
self.setViewportMargins(0, 0, 0, height + self._padding)
else:
self.setViewportMargins(0, 0, 0, 0)
super().updateGeometries()
self.adjustPositions()
def adjustPositions(self):
for index, editor in enumerate(self._editors):
if not isinstance(editor, QWidget):
continue
height = editor.sizeHint().height()
compensate_y = 0
compensate_x = 0
if type(editor) is QComboBox:
compensate_y = +2
elif type(editor) in (QWidget, Widget):
compensate_y = -1
elif type(editor) is QPushButton:
compensate_y = -1
elif type(editor) is QCheckBox:
compensate_y = 4
compensate_x = 4
editor.move(
self.sectionPosition(index) - self.offset() + 1 + compensate_x,
height + (self._padding // 2) + 2 + compensate_y,
)
editor.resize(self.sectionSize(index), height)
def filterText(self, index):
for editor in self._editors:
if hasattr(editor, "text") and callable(editor.text):
return editor.text()
return ""
def setFilterText(self, index, text):
for editor in self._editors:
if hasattr(editor, "setText") and callable(editor.setText):
editor.setText(text)
def clearFilters(self):
for editor in self._editors:
editor.clear()
def changebuttonsymbol(self):
nbtn = self.sender()
if nbtn.text() == "=":
nbtn.setText(">")
elif nbtn.text() == ">":
nbtn.setText("<")
else:
nbtn.setText("=")
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.filter_button = QPushButton("Filter")
self.filter_button.setCheckable(True)
self.filter_button.setChecked(True)
self.filter_button.clicked.connect(self.on_button_clicked)
self.view = QTableView()
self.view.horizontalHeader().setStretchLastSection(True)
button_layout = QHBoxLayout()
button_layout.addStretch()
button_layout.addWidget(self.filter_button)
layout = QVBoxLayout(self)
layout.addLayout(button_layout)
layout.addWidget(self.view)
header = FilterHeader(self.view)
self.view.setHorizontalHeader(header)
self.view.verticalHeader().setVisible(False)
# model = QStandardItemModel(self.view)
model = QStandardItemModel(5, 7, self.view)
for i in range(5):
for j in range(7):
item = QStandardItem(str(i + j))
model.setItem(i, j, item)
model.setHorizontalHeaderLabels("One Two Three Four Five Six Seven".split())
self.view.setModel(model)
header.setFilterBoxes(model.columnCount())
header.filterActivated.connect(self.handleFilterActivated)
self.on_button_clicked()
def handleFilterActivated(self):
header = self.view.horizontalHeader()
for index in range(header.count()):
if index != 4:
print(index, header.filterText(index))
else:
print("Button")
def on_button_clicked(self):
self.view.horizontalHeader().visible_editors = self.filter_button.isChecked()
I have some x and y coordinates that are being associated with some QGraphicItems, and then put into a QGraphicView. I recently noticed that while selecting the items with the rubberBand function that my dots were not orientated correctly.
I have 4 dots that overlap, and they are overlapping in the wrong spots, I did some debugging and found out my origin is starting in the upper left corner and Y+ is going down and my X+ is going toward the right.
I would like my system so that the Y+ will be getting larger as it goes up. I have taken a screenshot to show what the results of the code is currently ( I will paste below) and I put numbers next to the dots. Those numbers are representing how many dots are in that location. Also, I have put the X and Y directions.
Then Moved the numbers that are next to the dot around to represent what I am trying to achieve. Along with the X and Y orientation that I am Looking for.
I also noticed that it seems to be normalizing the coordinates. If I can retain the actually X and Y positions that would be for the best.
Here is the photo of what I currently Have
Here is what I want
Here is my current code
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
from math import sqrt
class LogObject(QObject):
hovered = pyqtSignal()
notHovered = pyqtSignal()
class Point(QGraphicsRectItem):
def __init__(self, x, y, name):
super(Point, self).__init__(QRectF(0, 0, 30, 30))
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
self.x = x
self.y = y
self.setBrush(QBrush(Qt.black))
self.setAcceptHoverEvents(True)
self.log = LogObject()
self.setPos(x, y)
def itemChange(self, change, value):
if change == self.ItemSelectedChange:
self.setBrush(QBrush(Qt.green) if value else QBrush(Qt.black))
return QGraphicsItem.itemChange(self, change, value)
def hoverEnterEvent(self, event):
self.setBrush(QColor("red"))
self.log.hovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
self.setBrush(QColor("black"))
self.log.notHovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def mousePressEvent(self, event):
print(self.name)
QGraphicsItem.mousePressEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self._scene = QGraphicsScene(self)
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
QTimer.singleShot(0, self.fitInView) # This is done so that it can fit into view on load
self.viewport().setCursor(Qt.ArrowCursor)
def setItems(self):
self.data = {'x': [-2414943.8686, -2417160.6592, -2417160.6592, -2417856.1783, -2417054.7618, -2416009.9966, -2416012.5232, -2418160.8952, -2418160.8952, -2416012.5232, -2417094.7694, -2417094.7694], 'y': [10454269.7008,
10454147.2672, 10454147.2672, 10453285.2456, 10452556.8132, 10453240.2808, 10455255.8752, 10455183.1912, 10455183.1912, 10455255.8752, 10456212.5959, 10456212.5959]}
maxX = max(self.data['x'])
minX = min(self.data['x'])
maxY = max(self.data['y'])
minY = min(self.data['y'])
distance = sqrt((maxX-minX)**2+(maxY-minY)**2)
self.area = QRectF(minX, minY, distance, distance)
self._scene.setSceneRect(QRectF(minX, -minY, distance, distance)) # Tried this but didn't seem to do anything
for i, (x, y) in enumerate(zip(self.data["x"], self.data["y"])):
p = Point(x, y, "Point__" + str(i))
p.log.hovered.connect(self.hoverChange)
p.log.notHovered.connect(self.notHoverChange)
self._scene.addItem(p)
self.setScene(self._scene)
def fitInView(self, scale=True):
rect = QRectF(self.area)
if not rect.isNull():
self.setSceneRect(rect)
unity = self.transform().mapRect(QRectF(0, 0, 1, 1))
self.scale(1 / unity.width(), 1 / unity.height())
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def setPoints(self):
self._zoom = 0
self.setItems()
self.setDragMode(self.ScrollHandDrag)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.fitInView()
else:
self._zoom = 0
def hoverChange(self):
self.viewport().setCursor(Qt.PointingHandCursor)
def notHoverChange(self):
self.viewport().setCursor(Qt.ArrowCursor)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(QEvent.MouseButtonPress,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mousePressEvent(self,handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
point= event.pos()
print(point)
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
if self.rubberBand.isVisible():
self.rubberBand.hide()
rect = self.rubberBand.geometry()
rect_scene = self.mapToScene(rect).boundingRect()
selected = self.scene().items(rect_scene)
if selected:
print(
"".join("Item: %s\n" % child.name for child in selected)
)
else:
print(" Nothing\n")
QGraphicsView.mouseReleaseEvent(self, event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ArrowCursor)
handmade_event = QMouseEvent(QEvent.MouseButtonRelease,QPointF(event.pos()),Qt.LeftButton,event.buttons(),Qt.KeyboardModifiers())
QGraphicsView.mouseReleaseEvent(self,handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(QRect(self.origin, event.pos()).normalized())
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self,event)
super(Viewer, self).mouseMoveEvent(event)
def hoverMoveEvent(self,event):
point=event.pos().toPoint()
print(point)
QGraphicsView.hoverMoveEvent(event)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText('Fit Into View')
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
self.viewer.fitInView()
def fitPoints(self):
self.viewer.fitInView()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 800, 600)
window.show()
sys.exit(app.exec_())
The idea is to scale the scene with (1, -1) that will make the Y axis invert. On the other hand QGraphicsView already has a fitInView method so I will use it since your current method can generate problems
class LogObject(QObject):
hovered = pyqtSignal()
notHovered = pyqtSignal()
class Point(QGraphicsRectItem):
def __init__(self, x, y, name):
super(Point, self).__init__(QRectF(0, 0, 30, 30))
self.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.name = name
self.setBrush(QBrush(Qt.black))
self.setAcceptHoverEvents(True)
self.log = LogObject()
self.setPos(x, y)
def itemChange(self, change, value):
if change == self.ItemSelectedChange:
self.setBrush(QBrush(Qt.green) if value else QBrush(Qt.black))
return QGraphicsItem.itemChange(self, change, value)
def hoverEnterEvent(self, event):
self.setBrush(QColor("red"))
self.log.hovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def hoverLeaveEvent(self, event):
self.setBrush(QColor("black"))
self.log.notHovered.emit()
QGraphicsItem.hoverMoveEvent(self, event)
def mousePressEvent(self, event):
print(self.name)
QGraphicsItem.mousePressEvent(self, event)
class Viewer(QGraphicsView):
photoClicked = pyqtSignal(QPoint)
rectChanged = pyqtSignal(QRect)
def __init__(self, parent):
super(Viewer, self).__init__(parent)
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
self.setMouseTracking(True)
self.origin = QPoint()
self.changeRubberBand = False
self._zoom = 0
self._empty = True
self.setScene(QGraphicsScene(self))
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setFrameShape(QFrame.NoFrame)
self.area = float()
self.setPoints()
self.viewport().setCursor(Qt.ArrowCursor)
QTimer.singleShot(0, self.reset_fit)
def setItems(self):
self.data = {
"x": [
-2414943.8686,
-2417160.6592,
-2417160.6592,
-2417856.1783,
-2417054.7618,
-2416009.9966,
-2416012.5232,
-2418160.8952,
-2418160.8952,
-2416012.5232,
-2417094.7694,
-2417094.7694,
],
"y": [
10454269.7008,
10454147.2672,
10454147.2672,
10453285.2456,
10452556.8132,
10453240.2808,
10455255.8752,
10455183.1912,
10455183.1912,
10455255.8752,
10456212.5959,
10456212.5959,
],
}
maxX = max(self.data["x"])
minX = min(self.data["x"])
maxY = max(self.data["y"])
minY = min(self.data["y"])
distance = sqrt((maxX - minX) ** 2 + (maxY - minY) ** 2)
self.area = QRectF(minX, minY, distance, distance)
self.scene().setSceneRect(
QRectF(minX, -minY, distance, distance)
) # Tried this but didn't seem to do anything
for i, (x, y) in enumerate(zip(self.data["x"], self.data["y"])):
p = Point(x, y, "Point__" + str(i))
p.log.hovered.connect(self.hoverChange)
p.log.notHovered.connect(self.notHoverChange)
self.scene().addItem(p)
def setPoints(self):
self.setItems()
self.setDragMode(self.ScrollHandDrag)
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
else:
factor = 0.8
self._zoom -= 1
if self._zoom > 0:
self.scale(factor, factor)
elif self._zoom == 0:
self.reset_fit()
else:
self._zoom = 0
def hoverChange(self):
self.viewport().setCursor(Qt.PointingHandCursor)
def notHoverChange(self):
self.viewport().setCursor(Qt.ArrowCursor)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rectChanged.emit(self.rubberBand.geometry())
self.rubberBand.show()
self.changeRubberBand = True
return
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ClosedHandCursor)
self.original_event = event
handmade_event = QMouseEvent(
QEvent.MouseButtonPress,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mousePressEvent(self, handmade_event)
super(Viewer, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
point = event.pos()
print(self.mapToScene(point))
if event.button() == Qt.LeftButton:
self.changeRubberBand = False
if self.rubberBand.isVisible():
self.rubberBand.hide()
rect = self.rubberBand.geometry()
rect_scene = self.mapToScene(rect).boundingRect()
selected = self.scene().items(rect_scene)
if selected:
print(
"".join("Item: %s\n" % child.name for child in selected)
)
else:
print(" Nothing\n")
QGraphicsView.mouseReleaseEvent(self, event)
elif event.button() == Qt.MidButton:
self.viewport().setCursor(Qt.ArrowCursor)
handmade_event = QMouseEvent(
QEvent.MouseButtonRelease,
QPointF(event.pos()),
Qt.LeftButton,
event.buttons(),
Qt.KeyboardModifiers(),
)
QGraphicsView.mouseReleaseEvent(self, handmade_event)
super(Viewer, self).mouseReleaseEvent(event)
def mouseMoveEvent(self, event):
if self.changeRubberBand:
self.rubberBand.setGeometry(
QRect(self.origin, event.pos()).normalized()
)
self.rectChanged.emit(self.rubberBand.geometry())
QGraphicsView.mouseMoveEvent(self, event)
super(Viewer, self).mouseMoveEvent(event)
def hoverMoveEvent(self, event):
point = event.pos().toPoint()
print(point)
QGraphicsView.hoverMoveEvent(event)
def reset_fit(self):
r = self.scene().itemsBoundingRect()
self.resetTransform()
self.setSceneRect(r)
self.fitInView(r, Qt.KeepAspectRatio)
self._zoom = 0
self.scale(1, -1)
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.viewer = Viewer(self)
self.btnLoad = QToolButton(self)
self.btnLoad.setText("Fit Into View")
self.btnLoad.clicked.connect(self.fitPoints)
VBlayout = QVBoxLayout(self)
VBlayout.addWidget(self.viewer)
HBlayout = QHBoxLayout()
HBlayout.setAlignment(Qt.AlignLeft)
HBlayout.addWidget(self.btnLoad)
VBlayout.addLayout(HBlayout)
def fitPoints(self):
self.viewer.reset_fit()
I'm trying to guess how to update the position of the edge when the nodes are moved or why it's not been automatically updated. I have found that the position of the nodes is not updated if I remove the self.update() from the mouseReleaseEvent but I don't know which is the mecanism to get the same on the edge class. Can anybody help me with this?
EDIT: With "position" I mean the value got by getPos() or getScenePos() for the QEdgeGraphicItem. It's printed on the output for the nodes and the Edge.
#!/usr/bin/env python
import math
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QGraphicsView, QGraphicsTextItem
class QEdgeGraphicItem(QtGui.QGraphicsItem):
Type = QtGui.QGraphicsItem.UserType + 2
def __init__(self, sourceNode, destNode, label=None):
super(QEdgeGraphicItem, self).__init__()
self.sourcePoint = QtCore.QPointF()
self.destPoint = QtCore.QPointF()
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges)
# self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
self.source = sourceNode
self.dest = destNode
self.label = QGraphicsTextItem("WHY IN THE HELL IS IT IN 0,0", self)
self.label.setParentItem(self)
self.label.setDefaultTextColor(QtCore.Qt.black)
self.source.addEdge(self)
self.dest.addEdge(self)
self.adjust()
def adjust(self):
if not self.source or not self.dest:
return
line = QtCore.QLineF(self.mapFromItem(self.source, 0, 0),
self.mapFromItem(self.dest, 0, 0))
self.prepareGeometryChange()
self.sourcePoint = line.p1()
self.destPoint = line.p2()
def boundingRect(self):
if not self.source or not self.dest:
return QtCore.QRectF()
extra = 2
return QtCore.QRectF(self.sourcePoint,
QtCore.QSizeF(self.destPoint.x() - self.sourcePoint.x(),
self.destPoint.y() - self.sourcePoint.y())).normalized().adjusted(-extra,
-extra,
extra,
extra)
def paint(self, painter, option, widget):
if not self.source or not self.dest:
return
# Draw the line itself.
line = QtCore.QLineF(self.sourcePoint, self.destPoint)
if line.length() == 0.0:
return
painter.setPen(QtGui.QPen(QtCore.Qt.black, 1, QtCore.Qt.SolidLine,
QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))
painter.drawLine(line)
painter.setBrush(QtCore.Qt.NoBrush)
painter.setPen(QtCore.Qt.red)
painter.drawRect(self.boundingRect())
print "Edge:"+str(self.scenePos().x()) + " " + str(self.scenePos().y())
class QNodeGraphicItem(QtGui.QGraphicsItem):
Type = QtGui.QGraphicsItem.UserType + 1
def __init__(self, label):
super(QNodeGraphicItem, self).__init__()
# self.graph = graphWidget
self.edgeList = []
self.newPos = QtCore.QPointF()
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable)
self.setFlag(QtGui.QGraphicsItem.ItemSendsGeometryChanges)
# self.setCacheMode(QtGui.QGraphicsItem.DeviceCoordinateCache)
# self.setZValue(1)
self.size = 40
self.border_width = 4
def type(self):
return QNodeGraphicItem.Type
def addEdge(self, edge):
self.edgeList.append(edge)
edge.adjust()
def boundingRect(self):
x_coord = y_coord = (-1*(self.size/2)) - self.border_width
width = height = self.size+23+self.border_width
return QtCore.QRectF(x_coord, y_coord , width,
height)
def paint(self, painter, option, widget):
x_coord = y_coord = -(self.size / 2)
width = height = self.size
painter.save()
painter.setBrush(QtGui.QBrush(QtGui.QColor(100, 0, 200, 127)))
painter.setPen(QtCore.Qt.black)
painter.drawEllipse(x_coord, y_coord, width, height)
painter.restore()
print "Node: " + str(self.scenePos().x()) + " " + str(self.scenePos().y())
def itemChange(self, change, value):
if change == QtGui.QGraphicsItem.ItemPositionHasChanged:
for edge in self.edgeList:
edge.adjust()
return super(QNodeGraphicItem, self).itemChange(change, value)
def mousePressEvent(self, event):
self.update()
super(QNodeGraphicItem, self).mousePressEvent(event)
def mouseReleaseEvent(self, event):
self.update()
super(QNodeGraphicItem, self).mouseReleaseEvent(event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
QtCore.qsrand(QtCore.QTime(0, 0, 0).secsTo(QtCore.QTime.currentTime()))
node1 = QNodeGraphicItem("Node1")
node2 = QNodeGraphicItem("Node2")
edge = QEdgeGraphicItem(node1,node2)
view = QGraphicsView()
view.setCacheMode(QtGui.QGraphicsView.CacheBackground)
view.setViewportUpdateMode(QtGui.QGraphicsView.BoundingRectViewportUpdate)
view.setRenderHint(QtGui.QPainter.Antialiasing)
view.setTransformationAnchor(QtGui.QGraphicsView.AnchorUnderMouse)
view.setResizeAnchor(QtGui.QGraphicsView.AnchorViewCenter)
view.scale(0.8, 0.8)
view.setMinimumSize(400, 400)
view.setWindowTitle("Example")
scene = QtGui.QGraphicsScene(view)
scene.setItemIndexMethod(QtGui.QGraphicsScene.NoIndex)
scene.setSceneRect(-400, -400, 800, 800)
view.setScene(scene)
scene.addItem(node1)
scene.addItem(node2)
scene.addItem(edge)
view.show()
sys.exit(app.exec_())
Thank you.
Does anybody know how I can implement circular progress bar on PyQt?
Also, I found an existing code:
http://sourceforge.net/projects/qroundprogressbar/
But, how it is in C++. How to use it for PyQt?
UPDATE: Using implementation of QRoundProgressBar below, I created a complete demo application with start button to demonstrate the QRoundProgressBar.
Save the QRoundProgressBar in circularprogressbar.py and create a new file in the same directory for the code below. Hope it helps others.
from circularprogressbar import QRoundProgressBar
import sys
from PyQt4.QtGui import *
from PyQt4 import QtCore, QtGui, Qt
from time import sleep
class TstWidget(QtGui.QWidget):
def __init__(self):
super(type(self), self).__init__()
self.bar = QRoundProgressBar()
self.bar.setFixedSize(300, 300)
self.bar.setDataPenWidth(3)
self.bar.setOutlinePenWidth(3)
self.bar.setDecimals(1)
self.bar.setFormat('%v | %p %')
# self.bar.resetFormat()
self.bar.setNullPosition(90)
self.bar.setBarStyle(QRoundProgressBar.StyleDonut)
self.bar.setDataColors([(0., QtGui.QColor.fromRgb(255,0,0)), (0.5, QtGui.QColor.fromRgb(255,255,0)), (1., QtGui.QColor.fromRgb(0,255,0))])
self.bar.setMaximun(100)
self.bar.setMinimun(0)
self.bar.setRange(0, 100)
self.bar.setValue(0)
button = QtGui.QPushButton("Start", self)
button.clicked.connect(self.on_start)
lay = QtGui.QVBoxLayout()
lay.addWidget(button)
lay.addWidget(self.bar)
self.setLayout(lay)
self.myLongTask = TaskThread()
self.myLongTask.notifyProgress.connect(self.on_progress)
def on_start(self):
self.myLongTask.start()
def on_progress(self, i):
self.bar.setValue(i)
class TaskThread(QtCore.QThread):
notifyProgress = QtCore.pyqtSignal(int)
def run(self):
for i in range(101):
self.notifyProgress.emit(i)
sleep(0.1)
def main():
app = QtGui.QApplication(sys.argv)
ex = TstWidget()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I have ported QRoundProgressBar on PyQt (and fixed some minor bugs):
from PyQt4 import QtCore, QtGui, Qt
class QRoundProgressBar(QtGui.QWidget):
StyleDonut = 1
StylePie = 2
StyleLine = 3
PositionLeft = 180
PositionTop = 90
PositionRight = 0
PositionBottom = -90
UF_VALUE = 1
UF_PERCENT = 2
UF_MAX = 4
def __init__(self):
super().__init__()
self.min = 0
self.max = 100
self.value = 25
self.nullPosition = self.PositionTop
self.barStyle = self.StyleDonut
self.outlinePenWidth =1
self.dataPenWidth = 1
self.rebuildBrush = False
self.format = "%p%"
self.decimals = 1
self.updateFlags = self.UF_PERCENT
self.gradientData = []
self.donutThicknessRatio = 0.75
def setRange(self, min, max):
self.min = min
self.max = max
if self.max < self.min:
self.max, self.min = self.min, self.max
if self.value < self.min:
self.value = self.min
elif self.value > self.max:
self.value = self.max
if not self.gradientData:
self.rebuildBrush = True
self.update()
def setMinimun(self, min):
self.setRange(min, self.max)
def setMaximun(self, max):
self.setRange(self.min, max)
def setValue(self, val):
if self.value != val:
if val < self.min:
self.value = self.min
elif val > self.max:
self.value = self.max
else:
self.value = val
self.update()
def setNullPosition(self, position):
if position != self.nullPosition:
self.nullPosition = position
if not self.gradientData:
self.rebuildBrush = True
self.update()
def setBarStyle(self, style):
if style != self.barStyle:
self.barStyle = style
self.update()
def setOutlinePenWidth(self, penWidth):
if penWidth != self.outlinePenWidth:
self.outlinePenWidth = penWidth
self.update()
def setDataPenWidth(self, penWidth):
if penWidth != self.dataPenWidth:
self.dataPenWidth = penWidth
self.update()
def setDataColors(self, stopPoints):
if stopPoints != self.gradientData:
self.gradientData = stopPoints
self.rebuildBrush = True
self.update()
def setFormat(self, format):
if format != self.format:
self.format = format
self.valueFormatChanged()
def resetFormat(self):
self.format = ''
self.valueFormatChanged()
def setDecimals(self, count):
if count >= 0 and count != self.decimals:
self.decimals = count
self.valueFormatChanged()
def setDonutThicknessRatio(self, val):
self.donutThicknessRatio = max(0., min(val, 1.))
self.update()
def paintEvent(self, event):
outerRadius = min(self.width(), self.height())
baseRect = QtCore.QRectF(1, 1, outerRadius-2, outerRadius-2)
buffer = QtGui.QImage(outerRadius, outerRadius, QtGui.QImage.Format_ARGB32)
buffer.fill(0)
p = QtGui.QPainter(buffer)
p.setRenderHint(QtGui.QPainter.Antialiasing)
# data brush
self.rebuildDataBrushIfNeeded()
# background
self.drawBackground(p, buffer.rect())
# base circle
self.drawBase(p, baseRect)
# data circle
arcStep = 360.0 / (self.max - self.min) * self.value
self.drawValue(p, baseRect, self.value, arcStep)
# center circle
innerRect, innerRadius = self.calculateInnerRect(baseRect, outerRadius)
self.drawInnerBackground(p, innerRect)
# text
self.drawText(p, innerRect, innerRadius, self.value)
# finally draw the bar
p.end()
painter = QtGui.QPainter(self)
painter.drawImage(0, 0, buffer)
def drawBackground(self, p, baseRect):
p.fillRect(baseRect, self.palette().background())
def drawBase(self, p, baseRect):
bs = self.barStyle
if bs == self.StyleDonut:
p.setPen(QtGui.QPen(self.palette().shadow().color(), self.outlinePenWidth))
p.setBrush(self.palette().base())
p.drawEllipse(baseRect)
elif bs == self.StylePie:
p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
p.setBrush(self.palette().base())
p.drawEllipse(baseRect)
elif bs == self.StyleLine:
p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
p.setBrush(Qt.Qt.NoBrush)
p.drawEllipse(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2))
def drawValue(self, p, baseRect, value, arcLength):
# nothing to draw
if value == self.min:
return
# for Line style
if self.barStyle == self.StyleLine:
p.setPen(QtGui.QPen(self.palette().highlight().color(), self.dataPenWidth))
p.setBrush(Qt.Qt.NoBrush)
p.drawArc(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2),
self.nullPosition * 16,
-arcLength * 16)
return
# for Pie and Donut styles
dataPath = QtGui.QPainterPath()
dataPath.setFillRule(Qt.Qt.WindingFill)
# pie segment outer
dataPath.moveTo(baseRect.center())
dataPath.arcTo(baseRect, self.nullPosition, -arcLength)
dataPath.lineTo(baseRect.center())
p.setBrush(self.palette().highlight())
p.setPen(QtGui.QPen(self.palette().shadow().color(), self.dataPenWidth))
p.drawPath(dataPath)
def calculateInnerRect(self, baseRect, outerRadius):
# for Line style
if self.barStyle == self.StyleLine:
innerRadius = outerRadius - self.outlinePenWidth
else: # for Pie and Donut styles
innerRadius = outerRadius * self.donutThicknessRatio
delta = (outerRadius - innerRadius) / 2.
innerRect = QtCore.QRectF(delta, delta, innerRadius, innerRadius)
return innerRect, innerRadius
def drawInnerBackground(self, p, innerRect):
if self.barStyle == self.StyleDonut:
p.setBrush(self.palette().alternateBase())
cmod = p.compositionMode()
p.setCompositionMode(QtGui.QPainter.CompositionMode_Source)
p.drawEllipse(innerRect)
p.setCompositionMode(cmod)
def drawText(self, p, innerRect, innerRadius, value):
if not self.format:
return
text = self.valueToText(value)
# !!! to revise
f = self.font()
# f.setPixelSize(innerRadius * max(0.05, (0.35 - self.decimals * 0.08)))
f.setPixelSize(innerRadius * 1.8 / len(text))
p.setFont(f)
textRect = innerRect
p.setPen(self.palette().text().color())
p.drawText(textRect, Qt.Qt.AlignCenter, text)
def valueToText(self, value):
textToDraw = self.format
format_string = '{' + ':.{}f'.format(self.decimals) + '}'
if self.updateFlags & self.UF_VALUE:
textToDraw = textToDraw.replace("%v", format_string.format(value))
if self.updateFlags & self.UF_PERCENT:
percent = (value - self.min) / (self.max - self.min) * 100.0
textToDraw = textToDraw.replace("%p", format_string.format(percent))
if self.updateFlags & self.UF_MAX:
m = self.max - self.min + 1
textToDraw = textToDraw.replace("%m", format_string.format(m))
return textToDraw
def valueFormatChanged(self):
self.updateFlags = 0;
if "%v" in self.format:
self.updateFlags |= self.UF_VALUE
if "%p" in self.format:
self.updateFlags |= self.UF_PERCENT
if "%m" in self.format:
self.updateFlags |= self.UF_MAX
self.update()
def rebuildDataBrushIfNeeded(self):
if self.rebuildBrush:
self.rebuildBrush = False
dataBrush = QtGui.QConicalGradient()
dataBrush.setCenter(0.5,0.5)
dataBrush.setCoordinateMode(QtGui.QGradient.StretchToDeviceMode)
for pos, color in self.gradientData:
dataBrush.setColorAt(1.0 - pos, color)
# angle
dataBrush.setAngle(self.nullPosition)
p = self.palette()
p.setBrush(QtGui.QPalette.Highlight, dataBrush)
self.setPalette(p)
Usage example:
class TstWidget(QtGui.QWidget):
def __init__(self):
super(type(self), self).__init__()
self.bar = QRoundProgressBar()
self.bar.setFixedSize(300, 300)
self.bar.setDataPenWidth(3)
self.bar.setOutlinePenWidth(3)
self.bar.setDonutThicknessRatio(0.85)
self.bar.setDecimals(1)
self.bar.setFormat('%v | %p %')
# self.bar.resetFormat()
self.bar.setNullPosition(90)
self.bar.setBarStyle(QRoundProgressBar.StyleDonut)
self.bar.setDataColors([(0., QtGui.QColor.fromRgb(255,0,0)), (0.5, QtGui.QColor.fromRgb(255,255,0)), (1., QtGui.QColor.fromRgb(0,255,0))])
self.bar.setRange(0, 300)
self.bar.setValue(260)
lay = QtGui.QVBoxLayout()
lay.addWidget(self.bar)
self.setLayout(lay)
#Alexandro, thank you for the code. It works nicely.
I found that it's best to set the min and max values as floats instead of int
self.min = 0.
self.max = 100.
self.value = 25.
Otherwise, the values won't update properly in python3 if the input value to valueToText() is also an integer, due to integer division
I have write this
class RoundProgress(QProgressBar):
def __init__(self,parent):
QProgressBar.__init__(self)
self.values = self.value()
self.values = (self.values*360)/100
self.parent = parent
self.setParent(parent)
self.n = self.value()
self.label = QLabel("<center>100%<center>")
self.label.setStyleSheet("color:red;")
self.label.setFont(QFont("courrier",math.sqrt(self.width())))
self.v = QVBoxLayout(self)
self.setLayout(self.v)
self.v.addWidget(self.label)
def setValue(self,n):
self.n = n
self.values = ((n*5650)/100)*(-1)
self.label.setText("<center>"+str(self.n)+"</center>")
def setNvalue(self,n):
self.n = n
self.values = ((n*5650)/100)*(-1)
self.label.setText("<center>"+str(self.n)+"</center>")
def paintEvent(self,event):
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
pen = QPen()
pen.setWidth(2)
pen.setColor(QColor("darkblue"))
painter.setPen(pen)
pen = QPen()
pen.setWidth(9)
pen.setColor(QColor("lightgrey"))
painter.setPen(pen)
painter.drawArc(5.1,5.1,self.width()-10,self.height()-10,1450,-5650)
#painter.drawEllipse(0,0,100,100)
painter.setBrush(QColor("lightblue"))
pen = QPen()
pen.setWidth(10)
pen.setColor(QColor("red"))
painter.setPen(pen)
painter.drawArc(5.1,5.1,self.width()-10,self.height()-10,1450,self.values)
self.update()
So I have a main window. I use setCentralWidget to set it to a class I made called mainWindowWidget to handle the UI.
I am now trying to add a graphics view widget I made to that but cannot seem to get anything to display. If I set the graphics view as the central widget I can see it and all my behaviour is working.
Do I need to do anything to get the graphics view to display within another widget?
Below are the sections of code I think are relevant to the question followed by the entire application. I am really new to PyQt and any guidance would be appreciated.
class mainWindowWidget(QtGui.QWidget):
grid = None
scene = None
def __init__(self):
self.initScene()
QtGui.QWidget.__init__(self)
def initScene(self):
self.grid = QtGui.QGridLayout()
'''Node Interface'''
self.scene = Scene(0, 0, 1280, 720, self)
self.view = QtGui.QGraphicsView()
self.view.setScene(self.scene)
self.grid.addWidget(self.view)
'''AttributeWindow'''
class MainWindowUi(QtGui.QMainWindow):
def __init__(self):
mainDataGraber = ind.dataGraber()
QtGui.QMainWindow.__init__(self)
self.setWindowTitle('RIS RIB Generator')
mainwindowwidget = mainWindowWidget()
self.setCentralWidget(mainwindowwidget)
This is the main GUI file for the application
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
This is the base py file for the GUI
Todo list
-----------------
- Pop up menu for adding new Nodes
- node connectivity
- create data structure for storing
"""
#import code mods
import sys
import uuid
import gtk, pygtk
from PyQt4 import QtGui, QtCore
from array import *
#import StyleMod
import RRG_NodeInterfaceGUIStyle as ns
import RRG_importNodeData as ind
"""
Base class for a node. Contains all the inilization, drawing, and containing inputs and outputs
"""
class node(QtGui.QGraphicsRectItem):
nid = 0
width = ns.nodeWidth
height = ns.nodeHeight
color = ns.nodeBackgroundColor
alpha = ns.nodeBackgroundAlpha
x = 90
y = 60
inputs=[]
outputs=[]
viewObj = None
isNode = True
scene = None
def __init__(self, n_x, n_y, n_width,n_height, n_scene):
QtGui.QGraphicsRectItem.__init__(self, n_x, n_y, n_width, n_height)
self.width = n_width
self.height = n_height
self.x = n_x
self.y = n_y
self.scene = n_scene
self.nid = uuid.uuid4()
print(self.nid)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.iniNodeData()
def mousePressEvent(self, e):
print("Square got mouse press event")
print("Event came to us accepted: %s"%(e.isAccepted(),))
a = []
self.scene.selectedNodes = a
self.scene.selectedNodes.append(self)
self.scene.selectedNodeID = self.nid
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
def mouseReleaseEvent(self, e):
#print("Square got mouse release event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsRectItem.mouseReleaseEvent(self, e)
"""
This is where inputs and outputs will be created based on node type
"""
def iniNodeData(self):
print('making node data')
for j in range(5):
this = self
x = input(this,0, 0+(j*10),self.scene)
self.inputs.append(x)
for k in range(5):
this = self
x = output(this, self.width-10, 0+(k*10),self.scene)
self.outputs.append(x)
def mouseMoveEvent(self, event):
self.scene.updateConnections()
QtGui.QGraphicsRectItem.mouseMoveEvent(self, event)
def nid(self):
return self.nid
"""
Nodes will evaluate from the last node to the first node, therefore inputs are evaluted
"""
class input(QtGui.QGraphicsRectItem):
currentConnectedNode = None
currentConnectedOutput = None
parentNode = None
width = 10
height = 10
x = 1
y = 1
color = 1
drawItem = None
isOutput = False
isNode = False
scene = None
points = []
line = None
def __init__(self, pnode, posX, posY, n_scene):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
self.scene = n_scene
QtGui.QGraphicsRectItem.__init__(self, self.x+self.parentNode.x, self.y+self.parentNode.y, self.width, self.height, self.parentNode)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
'''
This will handel connections. It determins if a connection is allowed aswell as creates them
'''
def mousePressEvent(self, e):
#print("Square got mouse press event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
emptyArray = []
if len(self.scene.selectedNodes) > 0 or len(self.scene.selectedNodes) > 1:
a = self.scene.selectedNodes[0]
if a.isNode == False:
if a.parentNode != self.parentNode:
if a.isOutput == True and self.isOutput == False:
print('Output and Input! line test runnin....')
currentConnectedOutput = a
currentConnectedNode = a.parentNode
if self.line != None:
self.line = None
self.scene.addConnection(self, a)
elif a.isOutput == True and self.isOutput == True:
print('Output and Output')
elif a.isOutput == False and self.isOutput == False:
print('Input and Input')
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
self.scene.selectedNodes.append(self)
else:
self.scene.selectedNodes.append(self)
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
'''
Output value from a node
'''
class output(QtGui.QGraphicsRectItem):
parentNode = None
width = 10
height = 10
x = 0
y = 0
isOutput = True
isNode = False
scene = None
def __init__(self, pnode, posX, posY, n_scene):
self.parentNode = pnode
self.x = posX
self.y = posY
self.color = 1
self.scene = n_scene
QtGui.QGraphicsRectItem.__init__(self, self.x+self.parentNode.x, self.y+self.parentNode.y, self.width, self.height, self.parentNode)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
'''
This will handel connections. It determins if a connection is allowed aswell as creates them
'''
def mousePressEvent(self, e):
#print("Square got mouse press event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
emptyArray = []
if len(self.scene.selectedNodes) > 0 or len(self.scene.selectedNodes) > 1:
a = self.scene.selectedNodes[0]
if a.isNode == False:
if a.parentNode != self.parentNode:
if a.isOutput == False and self.isOutput == True:
print('Input and Output!')
a.currentConnectedOutput = self
a.currentConnectedNode = self.parentNode
elif a.isOutput == True and self.isOutput == False:
print('Output and Input!')
elif a.isOutput == True and self.isOutput == True:
print('Output and Output')
elif a.isOutput == False and self.isOutput == False:
print('Input and Input')
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
else:
self.scene.selectedNodes = emptyArray
self.scene.selectedNodes.append(self)
else:
self.scene.selectedNodes.append(self)
QtGui.QGraphicsRectItem.mousePressEvent(self, e)
class connection(QtGui.QGraphicsLineItem):
usedNodeIDs = []
inputItem = None
outputItem = None
x1 = 0.0
y1 = 0.0
x2 = 0.0
y2 = 0.0
nid = None
scene = None
def __init__(self, n_inputItem, n_outputItemm, n_scene):
self.inputItem = n_inputItem
self.outputItem = n_outputItemm
self.usedNodeIDs.append(self.inputItem.parentNode.nid)
self.usedNodeIDs.append(self.outputItem.parentNode.nid)
self.updatePos()
QtGui.QGraphicsLineItem.__init__(self, self.x1, self.y1, self.x2, self.y2)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
self.scene = n_scene
self.nid = uuid.uuid4()
def update(self):
self.updatePos()
self.setLine(self.x1, self.y1, self.x2, self.y2)
def updatePos(self):
scenePosInput = QtGui.QGraphicsItem.pos(self.inputItem)
scenePosOutput = QtGui.QGraphicsItem.pos(self.outputItem)
scenePosInputNode = QtGui.QGraphicsItem.pos(self.inputItem.parentNode)
scenePosOutputNode = QtGui.QGraphicsItem.pos(self.outputItem.parentNode)
self.x1 = (scenePosInputNode.x()+self.inputItem.parentNode.x)+(scenePosInput.x()+self.inputItem.x) + ns.inputWidth/2
self.y1 = (scenePosInputNode.y()+self.inputItem.parentNode.y)+(scenePosInput.y()+self.inputItem.y) + ns.inputHeight/2
self.x2 = (scenePosOutputNode.x()+self.outputItem.parentNode.x)+(scenePosOutput.x()+self.outputItem.x) + ns.outputWidth/2
self.y2 = (scenePosOutputNode.y()+self.outputItem.parentNode.y)+(scenePosOutput.y()+self.outputItem.y) + ns.outputHeight/2
def mousePressEvent(self, e):
self.scene.selectedNodeID = self.nid
QtGui.QGraphicsLineItem.mousePressEvent(self, e)
'''
Check Click events on the scene Object
Also Stores the node data
'''
class Scene(QtGui.QGraphicsScene):
nodes = []
connections = []
selectedNodeID = None
def __init__(self, x, y, w, h, p):
super(Scene, self).__init__()
self.width = w
self.height = h
def mousePressEvent(self, e):
#print("Scene got mouse press event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsScene.mousePressEvent(self, e)
def mouseReleaseEvent(self, e):
#print("Scene got mouse release event")
#print("Event came to us accepted: %s"%(e.isAccepted(),))
QtGui.QGraphicsScene.mouseReleaseEvent(self, e)
def dragMoveEvent(self, e):
QtGui.QGraphicsScene.dragMoveEvent(self, e)
def updateConnections(self):
for connect in self.connections:
connect.update()
def addNode(self):
newNode = node(250,250,100,150, self)
self.addItem(newNode)
self.nodes.append(newNode)
def addPatternNode(self):
newNode = node(250,250,100,150, self)
self.addItem(newNode)
self.nodes.append(newNode)
def addConnection(self, n_inputItem, n_outputItem):
newConnection = connection(n_inputItem, n_outputItem, self)
self.addItem(newConnection)
self.connections.append(newConnection)
def keyPressEvent(self, e):
#Delete a node after it have been clicked on
#Use the node ID as the unique ID of the node to delete
if e.key() == QtCore.Qt.Key_Delete or e.key() == QtCore.Qt.Key_Backspace:
#If nothing selected
if self.selectedNodeID != None:
isConnection = False
for j in range(len(self.connections)):
if self.connections[j].nid == self.selectedNodeID:
isConnection = True
self.removeItem(self.connections[j])
self.connections.remove(self.connections[j])
if isConnection != True:
#first remove connections
rmItem = False
connectionsToRemove = []
for connect in self.connections:
rmItem = False
for nid in connect.usedNodeIDs:
if nid == self.selectedNodeID:
if rmItem == False:
connectionsToRemove.append(connect)
rmItem = True
for removeThis in connectionsToRemove:
self.connections.remove(removeThis)
self.removeItem(removeThis)
#now remove the nodes
for j in range(len(self.nodes)):
print(self.nodes[j].nid)
#figure out which node in our master node list must be deleted
if self.nodes[j].nid == self.selectedNodeID:
self.removeItem(self.nodes[j])
self.nodes.remove(self.nodes[j])
self.selectedNodeID = None
class mainWindowWidget(QtGui.QWidget):
grid = None
scene = None
def __init__(self):
self.initScene()
QtGui.QWidget.__init__(self)
def initScene(self):
self.grid = QtGui.QGridLayout()
'''Node Interface'''
self.scene = Scene(0, 0, 1280, 720, self)
self.view = QtGui.QGraphicsView()
self.view.setScene(self.scene)
self.grid.addWidget(self.view)
'''AttributeWindow'''
class MainWindowUi(QtGui.QMainWindow):
def __init__(self):
mainDataGraber = ind.dataGraber()
QtGui.QMainWindow.__init__(self)
self.setWindowTitle('RIS RIB Generator')
mainwindowwidget = mainWindowWidget()
self.setCentralWidget(mainwindowwidget)
exitAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close)
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'New Node', self)
newNodeAction.setStatusTip('Add a blank node')
newNodeAction.triggered.connect(mainwindowwidget.scene.addPatternNode)
nodeCreationActions = []
for nodeType in mainDataGraber.abstractNodeObjects:
nodeName = nodeType.nName
nodeType = nodeType.nType
#nodeStatusTip = nodeType.nhelp
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), nodeName, self)
newNodeAction.setStatusTip('nodeType.nhelp')
if nodeType == 'pattern':
newNodeAction.triggered.connect(mainwindowwidget.scene.addPatternNode)
nodeCreationActions.append(newNodeAction)
newNodeAction = QtGui.QAction(QtGui.QIcon('exit24.png'), 'New Node', self)
newNodeAction.setStatusTip('Add a blank node')
newNodeAction.triggered.connect(mainwindowwidget.scene.addPatternNode)
self.statusBar()
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newNodeAction)
nodeMenu = menubar.addMenu('&Nodes')
for action in nodeCreationActions:
nodeMenu.addAction(action)
fileMenu.addAction(exitAction)
'''
Start Point
'''
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindowUi()
win.show()
sys.exit(app.exec_())
Your issue is that you don't specify a parent for the QGridLayout (in the mainWindowWidget class), so it isn't attached to a widget. This results in the layout (and all widgets contained within it) not being visible. Adding a parent to the layout reveals a second problem in that you try and do things with the QWidget before calling __init__.
The corrected code is thus:
class mainWindowWidget(QtGui.QWidget):
grid = None
scene = None
def __init__(self):
QtGui.QWidget.__init__(self)
self.initScene()
def initScene(self):
self.grid = QtGui.QGridLayout(self)
'''Node Interface'''
self.scene = Scene(0, 0, 1280, 720, self)
self.view = QtGui.QGraphicsView()
self.view.setScene(self.scene)
self.grid.addWidget(self.view)
Note: For future questions requiring debugging help, please make a minimilistic example that is runnable. Don't just dump 90% of your code in a stack overflow post. It's not fun trying to dig through random code trying to cut out the missing imports so that it still reproduces the problem (fortunately it wasn't too difficult in this case). See How to create a Minimal, Complete, and Verifiable example.
Note 2: Why are you importing pygtk into a qt app?