I have QPixmaps inside cells of QTreeView using QStandardItemModel. I would like to be able to scale QPixmap when column is scaled (matching colum/cell width while keeping aspect ratio).
I can use scaled() method on QPixmap, but then I would have to figure out a trigger signal on column width change. Is there any simple and straightforward method ?
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.resize(400, 300)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.treeView = QtWidgets.QTreeView(self.centralwidget)
self.verticalLayout.addWidget(self.treeView)
MainWindow.setCentralWidget(self.centralwidget)
self.my_tree_model = MYTreeModel()
self.treeView.setModel(self.my_tree_model)
MainWindow.setCentralWidget(self.centralwidget)
class MYTreeModel(QtGui.QStandardItemModel):
def __init__(self):
super().__init__()
columns = ['data', 'thumbnail', 'data', 'data']
for row_id in range(3):
qrow_data = []
for col_id, column in enumerate(columns):
content = {}
if column == 'thumbnail':
image = QtGui.QImage('monkey.png')
item = QtGui.QStandardItem()
pxmap = QtGui.QPixmap.fromImage(image).scaled(50,50, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
item.setData(QtCore.QVariant(pxmap), QtCore.Qt.DecorationRole)
qrow_data.append(item)
else:
key = "data{0:s}{1:s}".format(str(row_id), str(col_id))
content[key] = QtGui.QStandardItem('my_data')
qrow_data.append(content[key])
self.appendRow(qrow_data)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication([])
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
One possible solution is to use a delegate:
class ThumbnailDelegate(QtWidgets.QStyledItemDelegate):
def initStyleOption(self, option, index):
super().initStyleOption(option, index)
option.icon = QtGui.QIcon()
option.features &= ~QtWidgets.QStyleOptionViewItem.HasDecoration
def paint(self, painter, option, index):
super().paint(painter, option, index)
mode = QtGui.QIcon.Normal
if not (option.state & QtWidgets.QStyle.State_Enabled):
mode = QtGui.QIcon.Disabled
elif option.state & QtWidgets.QStyle.State_Selected:
mode = QtGui.QIcon.Selected
state = (
QtGui.QIcon.On
if option.state & QtWidgets.QStyle.State_Open
else QtGui.QIcon.Off
)
pixmap = index.data(QtCore.Qt.DecorationRole).scaled(
option.rect.size(),
QtCore.Qt.KeepAspectRatio,
QtCore.Qt.SmoothTransformation,
)
icon = QtGui.QIcon(pixmap)
pixmap = icon.pixmap(pixmap.size(), mode, state)
painter.drawPixmap(option.rect, pixmap)
def sizeHint(self, option, index):
return index.data(QtCore.Qt.DecorationRole).size()
delegate = ThumbnailDelegate(self.treeView)
self.treeView.setItemDelegateForColumn(1, delegate)
Related
Am trying to use QStackedWidget() to switch to my next window but when i do that i get some errors that don't have when i run my ".py" files separately.
what my app should do is ... activate my group box with a click, then if i click the button, a new transparent window should pop-up with a mouse-cross listener, then when you click something it should stops returning the mouse cursor to normal and closing the transparent window.
window1.py
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtGui import QColor, QKeySequence, QIcon, QCursor
from window2 import *
class Ui_Form(QtWidgets.QWidget):
def __init__(self):
super(Ui_Form, self).__init__()
##if i use this method, it does not show my transparent window, also it shows an error that i dont get when run it separately.
def goToTransparentWindowMethod(self):
self.goToTransparentWindow = TransparentWindowClass()
myWindow.addWidget(self.goToTransparentWindow)
myWindow.setCurrentIndex(myWindow.currentIndex()+1)
def setupUi(self, myWindow):
myWindow.setObjectName("myWindow")
myWindow.resize(627, 327)
self.horizontalLayoutWidget = QtWidgets.QWidget(myWindow)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 300, 270))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.gpb_main = QtWidgets.QGroupBox(self.horizontalLayoutWidget)
self.gpb_main.setCheckable(True)
self.gpb_main.setChecked(False)
self.gpb_main.setObjectName("gpb_spell_main")
self.btn_main = QtWidgets.QPushButton(self.gpb_main)
self.btn_main.setGeometry(QtCore.QRect(10, 40, 88, 27))
self.btn_main.setObjectName("btn_main")
self.btn_main.clicked.connect(self.goToTransparentWindowMethod)
self.horizontalLayout.addWidget(self.gpb_main)
self.retranslateUi(myWindow)
QtCore.QMetaObject.connectSlotsByName(myWindow)
def retranslateUi(self, myWindow):
_translate = QtCore.QCoreApplication.translate
myWindow.setWindowTitle(_translate("myWindow", "Window1"))
self.gpb_main.setTitle(_translate("myWindow", "example"))
self.btn_main.setText(_translate("myWindow", "click me"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
myWindow = QtWidgets.QStackedWidget()
ui = Ui_Form()
ui.setupUi(myWindow)
myWindow.show()
sys.exit(app.exec_())
window2.py
from PyQt5 import QtCore, QtGui, QtWidgets
from pynput.mouse import Listener
import pyautogui
class TransparentWindowThreadClass(QtCore.QObject):
def __init__(self, parent=None):
super(TransparentWindowThreadClass, self).__init__()
#QtCore.pyqtSlot()
def on_click_main(self, x, y, button, pressed):
try:
if pressed:
self.pos_main = pyautogui.position()
self.get_rgb_main = pyautogui.pixel(self.pos_main[0], self.pos_main[1])
r = self.get_rgb_main.red
g = self.get_rgb_main.green
b = self.get_rgb_main.blue
self.get_rgb_main = r,g,b
Transparent_Window.unsetCursor()#error here when is called from window1
Transparent_Window.close()#error here when is called from window1
#Pressed
self.pressed_Msg = 'Pressed at x:{0} y:{1} RGB:{2}'.format(x, y, self.get_rgb_main)
print(self.pressed_Msg)
else:
#Released msg
self.released_Msg = 'Released at x:{0} y:{1} RGB:{2}'.format(x, y, self.get_rgb_main)
print(self.released_Msg)
if not pressed:
return False
except KeyboardInterrupt:
print('you pressed ctrl+c')
except NameError:
print("Error on_click_main")
except RuntimeError:
print('run time error')
except TypeError:
print('ype error')
except AttributeError:
print('Attribute Error')
#QtCore.pyqtSlot()
def loop_onclick_listener(self):
try:
with Listener(on_click=self.on_click_main) as listener:
listener.join()
except KeyboardInterrupt:
print('you pressed ctrl+c')
except NameError:
print("Error onclick_listener")
except RuntimeError:
print('run time error')
except TypeError:
print('ype error')
except AttributeError:
print('Attribute Error')
class TransparentWindowClass(QtWidgets.QWidget):
def __init__(self, parent=None):
super(TransparentWindowClass, self).__init__()
self.monitorResolution = pyautogui.size()
# create a QThread and start the thread that handles
self.thread = QtCore.QThread()
self.thread.start()
# create the worker without a parent so you can move it
self.worker = TransparentWindowThreadClass()## my custom thread class
# the worker is moved to another thread
self.worker.moveToThread(self.thread)
# if the thread started, connect it
self.thread.started.connect(self.worker.loop_onclick_listener)
def setupUi(self, Transparent_Window):
Transparent_Window.setObjectName("Transparent_Window")
Transparent_Window.resize(self.monitorResolution[0], self.monitorResolution[1])
Transparent_Window.setWindowFlags(QtCore.Qt.FramelessWindowHint)
Transparent_Window.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
Transparent_Window.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.retranslateUi(Transparent_Window)
QtCore.QMetaObject.connectSlotsByName(Transparent_Window)
def retranslateUi(self, Transparent_Window):
_translate = QtCore.QCoreApplication.translate
Transparent_Window.setWindowTitle(_translate("Transparent", "Transparent Window"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Transparent_Window = QtWidgets.QWidget()
ui = TransparentWindowClass()
ui.setupUi(Transparent_Window)
Transparent_Window.show()
sys.exit(app.exec_())
You are making things too complex.
Also, even assuming that no other possible solution could be used, you must remember that widgets are not thread-safe, and can not be directly accessed from external threads. The only safe and correct way to communicate between threads is by using signals and slots.
That said, there is no need for pyautogui nor pynput if you just want to get the color of a pixel on the screen.
If you want to grab a pixel on the screen, you can use the QScreen function grabWindow(), using 0 as window id (which matches the whole screen), and with a single-pixel area.
Then you can use grabMouse() to ensure that you always receive mouse events, even if the mouse is outside of the widget and no mouse button was being pressed. Note that grabMouse() can only work on visible widgets, so we need to "hide" the window by moving it off-screen.
Then, overriding mousePressEvent() you can use grabWindow() with the global position of the mouse, it will return a QPixmap that can be converted to a QImage and get the pixelColor() of the grab above.
The grabbing can be canceled by pressing Esc.
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class GrabTest(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 200)
self.button = QPushButton('Grab!')
self.colorLabel = QLabel('No color', alignment=Qt.AlignCenter)
self.colorLabel.setFixedSize(120, 30)
self.colorLabel.setStyleSheet('border: 1px solid black;')
layout = QVBoxLayout(self)
layout.addStretch()
layout.addWidget(self.button, alignment=Qt.AlignCenter)
layout.addWidget(self.colorLabel, alignment=Qt.AlignCenter)
layout.addStretch()
self.button.clicked.connect(self.startGrab)
def mousePressEvent(self, event):
if QWidget.mouseGrabber() == self:
self.getPixel(event.globalPos())
else:
super().mousePressEvent(event)
def keyPressEvent(self, event):
if QWidget.mouseGrabber() == self and event.key() == Qt.Key_Escape:
self.stopGrab()
else:
super().keyPressEvent(event)
def startGrab(self):
self.grabMouse(Qt.CrossCursor)
self.oldPos = self.pos()
deskRect = QRect()
for screen in QApplication.screens():
deskRect |= screen.geometry()
# move the window off screen while keeping it visible
self.move(deskRect.bottomRight())
def stopGrab(self):
self.releaseMouse()
self.move(self.oldPos)
def getPixel(self, pos):
screen = QApplication.screens()[0]
pixmap = screen.grabWindow(0, pos.x(), pos.y(), 1, 1)
color = pixmap.toImage().pixelColor(0, 0)
if color.lightnessF() > .5:
textColor = 'black'
else:
textColor = 'white'
self.colorLabel.setStyleSheet('''
color: {};
background: {};
'''.format(textColor, color.name()))
self.colorLabel.setText(color.name())
self.stopGrab()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
grabber = GrabTest()
grabber.show()
sys.exit(app.exec())
Please add a scrollable region to this code so that when I run the code, I can scroll down through the window and see the last items in the window and replace them.
As this is a drag and drop code I could not add scroll to it.
Moreover, during the drag and drop process, the scroll should work and does not interrupt the drag and drop process.
from PyQt5.QtWidgets import QApplication, QScrollArea, QHBoxLayout, QWidget, QLabel, QMainWindow, QVBoxLayout
from PyQt5.QtCore import Qt, QMimeData, pyqtSignal
from PyQt5.QtGui import QDrag, QPixmap
class DragItem(QLabel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setContentsMargins(25, 5, 25, 5)
self.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.setStyleSheet("border: 1px solid black;")
# Store data separately from display label, but use label for default.
self.data = self.text()
def set_data(self, data):
self.data = data
def mouseMoveEvent(self, e):
if e.buttons() == Qt.LeftButton:
drag = QDrag(self)
mime = QMimeData()
drag.setMimeData(mime)
pixmap = QPixmap(self.size())
self.render(pixmap)
drag.setPixmap(pixmap)
drag.exec_(Qt.MoveAction)
class DragWidget(QWidget):
"""
Generic list sorting handler.
"""
orderChanged = pyqtSignal(list)
def __init__(self, *args, orientation=Qt.Orientation.Horizontal, **kwargs):
super().__init__()
self.setAcceptDrops(True)
# Store the orientation for drag checks later.
self.orientation = orientation
if self.orientation == Qt.Orientation.Vertical:
self.blayout = QVBoxLayout()
else:
self.blayout = QHBoxLayout()
self.setLayout(self.blayout)
def dragEnterEvent(self, e):
e.accept()
def dropEvent(self, e):
pos = e.pos()
widget = e.source()
for n in range(self.blayout.count()):
# Get the widget at each index in turn.
w = self.blayout.itemAt(n).widget()
if self.orientation == Qt.Orientation.Vertical:
# Drag drop vertically.
drop_here = pos.y() < w.y() + w.size().height() // 2
else:
# Drag drop horizontally.
drop_here = pos.x() < w.x() + w.size().width() // 2
if drop_here:
# We didn't drag past this widget.
# insert to the left of it.
self.blayout.insertWidget(n-1, widget)
self.orderChanged.emit(self.get_item_data())
break
e.accept()
def add_item(self, item):
self.blayout.addWidget(item)
def get_item_data(self):
data = []
for n in range(self.blayout.count()):
# Get the widget at each index in turn.
w = self.blayout.itemAt(n).widget()
data.append(w.data)
return data
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.drag = DragWidget(orientation=Qt.Orientation.Vertical)
self.scroll = QScrollArea() ###########
self.blayout = QHBoxLayout()
self.widget = QWidget()
for n, l in enumerate(['Art', 'Boo', 'EOA', 'Hel', \
'Hyg', 'Lei', 'Lei','Lei','Lei','Lei','Lei','Lei','Lei','Lei','Lei','Lei','Lei', 'Med',\
'Nut','Nut','Nut','Rel','Rel','Rel','Sle','SLN','Spo','Spo','Spo','Spo','Spo','Thi','Thi',\
'Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor','Wor']):
item = DragItem(l)
item.set_data(n) # Store the data.
self.drag.add_item(item)
def myFun():
print('hi', self.drag.get_item_data())
# Print out the changed order.
# self.drag.orderChanged.connect(print)
# add by me
self.drag.orderChanged.connect(myFun)
#Scroll Area Properties
self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.scroll.setWidgetResizable(True)
self.scroll.setWidget(self.widget)
container = QWidget()
layout = QVBoxLayout()
layout.addStretch(1)
layout.addWidget(self.drag)
layout.addStretch(1)
container.setLayout(layout)
self.setCentralWidget(container)
# self.setCentralWidget(self.scroll)
app = QApplication([])
w = MainWindow()
w.show()
app.exec_()
I have a program that dynamically creates tabs with buttons on them, when the user clicks button, I want it to give me the button_id (number that corresponds to the tab index).
I understand that you can do something like tabwidget.currentIndex() to get index of tab being used, but I don't want that as I will eventually have a method that iterates through the number of tabs and access each button without selecting the tabs as shown below.
for i in range(1,self.tabWidget.count()):
self.tabWidget.widget(i).stagematch.click()
For example:
If user clicks 'Clear Text' button on 'Tab 2' then I want it to give me the number 2 back.
How can I accomplish this without using the currentIndex() method for the tabs
Test code:
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
testbutton = QtWidgets.QPushButton('Clear Text')
grid.addWidget(testbutton, 2, 2)
testbutton.clicked.connect(self.tab_match)
#testbutton.clicked.connect(self.button_id)
def button_id(self):
sender = self.sender()
print(sender.text()) # Gives text of button, i'd like a number that corresponds to the tab# that called it
def tab_match(self,button_id):
#Do something with button ID here
pass
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
Try it:
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent # +
self.button_id = 0 # +
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
testbutton = QtWidgets.QPushButton('Clear Text')
grid.addWidget(testbutton, 2, 2)
testbutton.clicked.connect(self.tab_match)
self.parent.currentChanged.connect(self.qtabwidget_currentchanged) # +
def tab_match(self):
#Do something with button ID here
print("\ndef tab_match: button_id-> {}".format(self.button_id)) # +
#QtCore.pyqtSlot(int)
def qtabwidget_currentchanged(self, index): # +
self.button_id = index
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
self.button_id = 0
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
i have a page containing two tabs.i want to add a fadeIn effect when i change the tabs.Is that possible?
import sys
from PyQt4.QtCore import QTimeLine
from PyQt4.QtGui import *
class FaderWidget(QWidget):
def __init__(self, old_widget, new_widget):
QWidget.__init__(self, new_widget)
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.pixmap_opacity = 1.0
self.timeline = QTimeLine()
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
self.timeline.setDuration(333)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
painter.end()
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.repaint()
class StackedWidget(QStackedWidget):
def __init__(self, parent = None):
QStackedWidget.__init__(self, parent)
def setCurrentIndex(self, index):
self.fader_widget = FaderWidget(self.currentWidget(), self.widget(index))
QStackedWidget.setCurrentIndex(self, index)
def setPage1(self):
self.setCurrentIndex(0)
def setPage2(self):
self.setCurrentIndex(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
stack = StackedWidget()
stack.addWidget(QCalendarWidget())
editor = QTextEdit()
editor.setPlainText("Hello world! "*100)
stack.addWidget(editor)
page1Button = QPushButton("Page 1")
page2Button = QPushButton("Page 2")
page1Button.clicked.connect(stack.setPage1)
page2Button.clicked.connect(stack.setPage2)
layout = QGridLayout(window)
layout.addWidget(stack, 0, 0, 1, 2)
layout.addWidget(page1Button, 1, 0)
layout.addWidget(page2Button, 1, 1)
window.show()
sys.exit(app.exec_())
this is code that shows some fade effect but i m getting nothing from it and how it works and how to implement on tabs. it will be really appreciable if someone could help me implement it on tabs as well.
thanks in advance.
With the same logic as the code you show, each widget will be placed inside a QStackedWidget, where one of them will be the widget that will be displayed and the other will be the FaderWidget.
class FaderWidget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.pixmap_opacity = None
self.timeline = QTimeLine(333, self)
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
def start(self, old_widget, new_widget):
self.pixmap_opacity = 1.0
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
if self.pixmap_opacity:
QWidget.paintEvent(self, event)
painter = QPainter(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.update()
class FaderTabWidget(QTabWidget):
def __init__(self, parent=None):
QTabWidget.__init__(self, parent)
self.currentChanged.connect(self.onCurrentIndex)
self.last = -1
self.current = self.currentIndex()
def onCurrentIndex(self, index):
self.last = self.current
self.current = self.currentIndex()
if self.widget(self.last):
self.widget(self.last).setCurrentIndex(1)
old_widget = self.widget(self.last).widget(0)
current_widget = self.widget(self.current).widget(0)
fade = self.widget(self.current).widget(1)
fade.start(old_widget, current_widget)
def addTab(self, widget, text):
stack = QStackedWidget(self)
stack.addWidget(widget)
fade = FaderWidget(self)
fade.timeline.finished.connect(lambda: stack.setCurrentIndex(0))
stack.addWidget(fade)
stack.setCurrentIndex(0 if self.currentIndex() == -1 else 1)
QTabWidget.addTab(self, stack, text)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
tabWidget = FaderTabWidget()
tabWidget.addTab(QCalendarWidget(), "Tab1")
editor = QTextEdit()
editor.setPlainText("Hello world! " * 100)
tabWidget.addTab(editor, "Tab2")
layout = QVBoxLayout(window)
layout.addWidget(tabWidget)
window.show()
sys.exit(app.exec_())
I'm creating a simple PySide application that also uses MatPlotLib. However, when I add the figure into a QFrame, the figure doesn't automatically fit to the frame:
My graph is created using the following code:
class GraphView(gui.QWidget):
def __init__(self, name, title, graphTitle, parent = None):
super(GraphView, self).__init__(parent)
self.name = name
self.graphTitle = graphTitle
self.dpi = 100
self.fig = Figure((5.0, 3.0), dpi = self.dpi, facecolor = (1,1,1), edgecolor = (0,0,0))
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.Title = gui.QLabel(self)
self.Title.setText(title)
self.layout = gui.QVBoxLayout()
self.layout.addStretch(1)
self.layout.addWidget(self.Title)
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
def UpdateGraph(self, data, title = None):
self.axes.clear()
self.axes.plot(data)
if title != None:
self.axes.set_title(title)
self.canvas.draw()
And it's added to the main Widget like so:
# Create individual Widget/Frame (fftFrame)
fftFrame = gui.QFrame(self)
fftFrame.setFrameShape(gui.QFrame.StyledPanel)
self.FFTGraph = GraphView('fftFrame', 'FFT Transform:', 'FFT Transform of Signal', fftFrame)
Here's a working code sample that shows you how to get it working. I first thought it was because of the stretch you added to the layout, which will use up the additional space around the other widgets. But when I removed it, it still wouldn't resize. The 'easy' solution is to add a resizeEvent, which lets you define the size of your GraphView widget. In this case I just set its geometry to be that of the QFrame, though you might want to add some padding and make sure you set a sensible minimum size for the QFrame.
from PySide import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.fft_frame = FftFrame(self)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.fft_frame)
self.setLayout(self.layout)
self.setCentralWidget(self.fft_frame)
class FftFrame(QtGui.QFrame):
def __init__(self, parent=None):
super(FftFrame, self).__init__(parent)
self.setFrameShape(QtGui.QFrame.StyledPanel)
self.parent = parent
self.graph_view = GraphView('fftFrame', 'FFT Transform:', 'FFT Transform of Signal', self)
def resizeEvent(self, event):
self.graph_view.setGeometry(self.rect())
class GraphView(QtGui.QWidget):
def __init__(self, name, title, graph_title, parent = None):
super(GraphView, self).__init__(parent)
self.name = name
self.graph_title = graph_title
self.dpi = 100
self.fig = Figure((5.0, 3.0), dpi = self.dpi, facecolor = (1,1,1), edgecolor = (0,0,0))
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self)
self.Title = QtGui.QLabel(self)
self.Title.setText(title)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.Title)
self.layout.addWidget(self.canvas)
self.layout.setStretchFactor(self.canvas, 1)
self.setLayout(self.layout)
self.canvas.show()
def update_graph(self, data, title = None):
self.axes.clear()
self.axes.plot(data)
if title != None:
self.axes.set_title(title)
self.canvas.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()