I have a python project that outputs several Matplotlib figures; each figure contains several charts. The problem that project launches about 15 figures (windows) every run, which I can not reduce.
Is it possible to concatenate all these figures (windows) to a single tabbed window so that each tab represents one figure?
Any help is much appreciated.
Thanks in advance
Workaround
Thanks to #mobiusklein comments below he suggested a workaround, to export the figures as myltipage pdf file as shown here.
Important note about the multipage pdf example mentioned above.
I tried it, but I got an error regarding the LaTeX use in matplotlib. Because fixing this error is beyond the scope of this question, so I suggest if it occurs to anyone, to set plt.rc('text', usetex=False) instead of usetex=True
I still hope if someone have other solution or workaround to post it for the benefit of others.
I wrote a simple wrapper for matplotlib that does something like you're describing. You need pyqt5 for it to work though.
Here is the code, you build a plotWindow object and feed it figure handles. It'll create a new tab for each figure.
import matplotlib
# prevent NoneType error for versions of matplotlib 3.1.0rc1+ by calling matplotlib.use()
# For more on why it's nececessary, see
# https://stackoverflow.com/questions/59656632/using-qt5agg-backend-with-matplotlib-3-1-2-get-backend-changes-behavior
matplotlib.use('qt5agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTabWidget, QVBoxLayout
import matplotlib.pyplot as plt
import sys
class plotWindow():
def __init__(self, parent=None):
self.app = QApplication(sys.argv)
self.MainWindow = QMainWindow()
self.MainWindow.__init__()
self.MainWindow.setWindowTitle("plot window")
self.canvases = []
self.figure_handles = []
self.toolbar_handles = []
self.tab_handles = []
self.current_window = -1
self.tabs = QTabWidget()
self.MainWindow.setCentralWidget(self.tabs)
self.MainWindow.resize(1280, 900)
self.MainWindow.show()
def addPlot(self, title, figure):
new_tab = QWidget()
layout = QVBoxLayout()
new_tab.setLayout(layout)
figure.subplots_adjust(left=0.05, right=0.99, bottom=0.05, top=0.91, wspace=0.2, hspace=0.2)
new_canvas = FigureCanvas(figure)
new_toolbar = NavigationToolbar(new_canvas, new_tab)
layout.addWidget(new_canvas)
layout.addWidget(new_toolbar)
self.tabs.addTab(new_tab, title)
self.toolbar_handles.append(new_toolbar)
self.canvases.append(new_canvas)
self.figure_handles.append(figure)
self.tab_handles.append(new_tab)
def show(self):
self.app.exec_()
if __name__ == '__main__':
import numpy as np
pw = plotWindow()
x = np.arange(0, 10, 0.001)
f = plt.figure()
ysin = np.sin(x)
plt.plot(x, ysin, '--')
pw.addPlot("sin", f)
f = plt.figure()
ycos = np.cos(x)
plt.plot(x, ycos, '--')
pw.addPlot("cos", f)
pw.show()
This is also posted at: https://github.com/superjax/plotWindow
Hopefully this could be a good starting point for your application.
The backend you choose to use for matplotlib controls how each figure is displayed. Some backends just render figures to file, while others like the tk, qt, or gtk backends render figures in graphical windows. Those backends determine what functionality those GUI windows have.
The existing backends don't support the type of tabbed navigation you're looking for. Someone else here implemented this using Qt4.
You might also try writing your own report files with PDF or HTML which would let you more easily write more complex image arrangements with simpler libraries.
Something that is functionally similar might be implemented using the widgets. For example, provide a row of buttons, one button for each "tab", and repaint the graphical portion of the window in response to each button.
The buttons example as a workaround is creative. As another stated you can use PyQt to create a tabbed window.
It is for this reason I use PyQtGraph. PyQtGraph only uses PyQt as a backend and therefor "natively" supports both tabbed windows and "docks". Docks allow for movable tabs and splits as well as breaking off a tab or split to a new floating window.
In general, PyQtGraph's docks provide a method for organizing your graphs/plots/images that I haven't been able to get with other libraries.
Bokeh offers tabbed windows through their Panels and Tabs widgets.
I know it is not always feasible to move away from matplotlib but I felt like there was a lack of representation of libraries which have considered and implemented tools specifically for your use case.
I shared my code that allows docking and tabbing with drag-n-drop (qt docking system). It acts as a matplotlib backend, so it's easy to integrate.
https://github.com/peper0/mpldock
I recently released a python package which contains a class called DockablePlotWindow which provides a solution similar to superjax's answer but which provides a little more flexibility to organize how the plots are initially displayed.
I'm interested to continue improving this package so feel free to open pull requests or issues on github. You can find information about it here: https://github.com/nanthony21/mpl_qt_viz
and here:
https://nanthony21.github.io/mpl_qt_viz/
Related
As a continuation of this question of mine:
pyqtgraph for plotting multiple data lists
I managed to use pyqtgraph to export my plot to a file. But i still get the window that pyqtgraph spawns in order to try to create the plot there. This window now shows nothing, it is empty and white. When i use regular python console, after a while this window disappears, but if i use Ipython, the window says "Not responding" and when i close it Ipython says "Kernel died, restarting".
Is there a way to completely disable this pyqtgraph window and only use the output file to create the plot, in order for it to work correctly without errors?
I used to do this with matplotlib (which had the same window popping up, but if you used command matplotlib.use('Agg'), to change the backend, then the window stopped popping.
Oh my... i just figured it out! My first answer on SO, don't be too harsh on me.
First, make sure you are creating your pyqtgraph graph in a constructor(init function) of a class. Call it there once and immediately hide it (that was the complicated part for me).
Here is an example code:
import numpy as np
import pyqtgraph as pg
import pyqtgraph.exporters
class MyPlotClass():
def __init__(self):
self.windowplt = pg.plot()
self.windowplt.win.hide()
def savePlots(self):
x = np.arange(0, 256)
y = np.arange(0, 256)
self.windowplt.plot(x, y)
exporter = pg.exporters.ImageExporter(self.windowplt.plotItem)
exporter.params.param('width').setValue(256, blockSignal=exporter.widthChanged)
exporter.params.param('height').setValue(256, blockSignal=exporter.heightChanged)
for i in np.arange(0,10):
exporter.export('./fileName' + str(i) + '.png')
print(i)
if __name__ == "__main__":
saveMyFiles = MyPlotClass()
saveMyFiles.savePlots()
Only one window WILL appear for a shot duration and hide itself immediately.
I know your Question is old, but it might help anyone in the future. I was searching for the solution for the whole day now.
As mentioned in your previous thread pyqtgraph for plotting multiple data lists the ImageExporter.py bug still exists. Insted of changing the code of the pyqtgraph library you can work around it by setting both width and height yourself (as in the code above).
exporter.params.param('width').setValue(256, blockSignal=exporter.widthChanged)
exporter.params.param('height').setValue(256, blockSignal=exporter.heightChanged)
Consider the following python3 PyQt code to display an interactive matplotlib graph with toolbar
import sys, sip
import numpy as np
from PyQt5 import QtGui, QtWidgets
from PyQt5.Qt import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
app = QApplication(sys.argv)
top = QWidget()
fig = plt.figure()
ax = fig.gca()
x = np.linspace(0,5,100)
ax.plot(x,np.sin(x))
canvas = FigureCanvas(fig)
toolbar = NavigationToolbar(canvas, top)
def pick(event):
if (event.xdata is None) or (event.ydata is None): return
ax.plot([0,event.xdata],[0,event.ydata])
canvas.draw()
canvas.mpl_connect('button_press_event', pick)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(toolbar)
layout.addWidget(canvas)
top.setLayout(layout)
top.show()
app.exec_()
Now I'd like to achieve the same by using PyQt with QML instead. I have some experience with creating QML GUIs in C++ and I really like the fact that the layout code is nicely separated from the core logic of the code.
I have found several examples on how to show plots in PyQt and on how to use Python with QML, but nothing that combines the two.
To start off, my python and QML snippets look as follows:
Python:
import sys, sip
import numpy as np
from PyQt5 import QtGui, QtWidgets
from PyQt5.Qt import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load(QUrl('layout.qml'))
root = engine.rootObjects()[0]
root.show()
sys.exit(app.exec_())
Layout:
import QtQuick 2.7
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 400
height: 400
Canvas {
// canvas may not be the right choice here
id: mycanvas
anchors.fill: parent
}
}
But I am quite lost on how to continue.
More concretely, the question would be: Is there a way to display an interactive matplotlib plot in QML (by interactive I mean not just a figure that has been saved as an image, ideally with the standard toolbar for zoom etc.)
Can anyone help? Or is the combination of QML and plots just simply discouraged (this question suggests python and QML should work together quite well)?
I don't have a full solution, but if you're OK with just displaying charts and the fact that you'll have to provide any interactive controls by yourself, then there's a reasonably simple way to do that.
First of all, you will need to convert your matplotlib chart into a QImage. Fortunately doing so is surprisingly easy. The canonical backend (renderer) for matplotlib is *Agg`, and it allows you to render your Figure into a memory. Just make a suitable Canvas object for you Figure, then call .draw(). The QImage constructor will take generated data directly as inputs.
canvas = FigureCanvasAgg(figure)
canvas.draw()
img = QtGui.QImage(canvas.buffer_rgba(), *canvas.get_width_height(), QtGui.QImage.Format_RGBA8888).copy()
The Qt way to provide that image into QML is to use QQuickImageProvider. It will get "image name" as input from QML and should provide a suitable image as output. This allows you to serve all matplotlib charts in your app with just one Image provider. When I was working on a small visualization app for internal use, I ended up with a code like this:
import PyQt5.QtCore as QtCore
import PyQt5.QtGui as QtGui
import PyQt5.QtQuick as QtQuick
import PyQt5.QtQml as QtQml
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg
class MatplotlibImageProvider(QtQuick.QQuickImageProvider):
figures = dict()
def __init__(self):
QtQuick.QQuickImageProvider.__init__(self, QtQml.QQmlImageProviderBase.Image)
def addFigure(self, name, **kwargs):
figure = Figure(**kwargs)
self.figures[name] = figure
return figure
def getFigure(self, name):
return self.figures.get(name, None)
def requestImage(self, p_str, size):
figure = self.getFigure(p_str)
if figure is None:
return QtQuick.QQuickImageProvider.requestImage(self, p_str, size)
canvas = FigureCanvasAgg(figure)
canvas.draw()
w, h = canvas.get_width_height()
img = QtGui.QImage(canvas.buffer_rgba(), w, h, QtGui.QImage.Format_RGBA8888).copy()
return img, img.size()
Whenever I need to draw a plot in python code, I just create Figure using this addFigure to give it a name and let the Qt to know about it. Once you got Figure, rest of matplotlib drawing happens exactly as usual. Make axes and plot.
self.imageProvider = MatplotlibImageProvider()
figure = self.imageProvider.addFigure("eventStatisticsPlot", figsize=(10,10))
ax = figure.add_subplot(111)
ax.plot(x,y)
Then in QML code I can simply refer matplotlib image by name ("eventStatisticsPlot")
Image {
source: "image://perflog/eventStatisticsPlot"
}
Note that URL is prefixed by "image://" to tell QML that we need to get image from QQuickImageProvider and includes name ("perflog") of a particular provider to use. For this stuff to work we need to register our provider during QML initialization with a call to addImageProvider. For example,
engine = QtQml.QQmlApplicationEngine()
engine.addImageProvider('perflog', qt_model.imageProvider)
engine.load(QtCore.QUrl("PerflogViewer.qml"))
At this point you should be able to see static graphs shown, but they will not be updated properly because Image component in QML assumes that image that we provide does not change. I found no good solution for it, but an ugly workaround is fairly simple. I added a signal called eventStatisticsPlotChanged to my helper class that exposes Python app data to QML and .emit() it whenever the relevant plot is changed. E.g. here's a chunk of code where I get data from QML on a time interval selected by user.
#QtCore.pyqtSlot(float, float)
def selectTimeRange(self, min_time, max_time):
self.selectedTimeRange = (min_time, max_time)
_, ax, _ = self.eventStatisticsPlotElements
ax.set_xlim(*self.selectedTimeRange)
self.eventStatisticsPlotChanged.emit()
See that .emit() in the end? In QML this event forces image to reload URL like this:
Image {
source: "image://perflog/eventStatisticsPlot"
cache: false
function reload() { var t = source; source = ""; source = t; }
}
Connections {
target: myDataSourceObjectExposedFromPython
onEventStatisticsPlotChanged: eventStatisticsPlot.reload()
}
So whenever user moves a control, following happens:
QML sends updated time interval to my data source via selectTimeRange() call
My code calls .set_xlim on appopriate matplotlib object and emit() a signal to notify QML that chart changed
QML queries my imageProvider for updated chart image
My code renders matplotlib chart into new QImage with Agg and passes it to Qt
QML shows that image to user
It might sound a bit complicated, but its actually easy to design and use.
Here's an example of how all this looks in our small visualization app. That's pure Python + QML, with pandas used to organize data and matplotlib to show it. Scroll-like element on bottom of the screen essentially redraws chart on every event and it happens so fast that it feels real-time.
I also tried to use SVG as a way to feed vector image into QML. It's also possible and it also works. Matplotlib offers SVG backend (matplotlib.backends.backend_svg) and Image QML component support inline SVG data as a Source. The SVG data is text so it can be easily passed around between python and QML. You can update (source) field with new data and image will redraw itself automatically, you can rely on data binding. It could've worked quite well, but sadly SVG support in Qt 4 and 5 is poor. Clipping is not supported (charts will go out of the axes); resizing Image does not re-render SVG but resizes pixel image of it; changing SVG causes image to blink; performance is poor. Maybe this will change one day later, but for now stick to agg backend.
I really love design of both matlpotlib and Qt. It's smart and it meshes well without too much effort or boilerplate code.
It's in a fairly basic state, but https://github.com/jmitrevs/matplotlib_backend_qtquick provides a workable model to start from.
To quickly summarize the gist of the example provided with it:
The library provides the types FigureCanvasQtQuickAgg and NavigationToolbar2QtQuick.
The FigureCanvasQtQuickAgg type is registered with QML:
QtQml.qmlRegisterType(FigureCanvasQtQuickAgg, "Backend", 1, 0, "FigureCanvas")
This allows you to use it from within QML:
FigureCanvas {
id: mplView
objectName : "figure"
dpi_ratio: Screen.devicePixelRatio
anchors.fill: parent
}
The objectName property allows the canvas instance to be found from within the Python code.
The toolbar is made out of QML buttons,
In the demo, they provide a DisplayBridge Python class that is linked to the canvas and is responsible for the actual plotting and for forwarding events from the various toolbar buttons
Internally, the FigureCanvasQtQuickAgg backend is a QQuickPaintedItem. In its paint function it copies data from the renderer attribute of the matplotlib FigureCanvasAgg base class into a QImage and the QImage is then painted. This is a fairly similar design to how the QWidget version of matplotlib works.
I have a basic QTableWidget, created with this python code:
from silx.gui import qt
app = qt.QApplication([])
qtw = qt.QTableWidget()
qtw.show()
qtw.setColumnCount(8)
qtw.setRowCount(7)
app.exec_()
The from silx.gui import qt line is just a wrapper that finds out the installed qt wrapper (PyQt4, PyQt5 or PySide) and flattens the qt namespace.
The resulting table has a strange behavior when I edit a cell: as expected, the old text is highligted when I double-click the cell, but the unusual behavior is that the old text remains visible and the new text overlaps with the old one while I'm typing it, until I press enter or I click another cell.
I would like the old text to disappear as soon as I start typing the new one. I know it's possible, because I have an example of program that features a qTableWidget with the behavior I would like to have.
But I cannot find where in that program the cell editing behavior is altered. How can I do this?
Example of "spam" and "eggs" overlayed.
[
EDIT: the code sample without the wrapper business
from PyQt5.Qt import QApplication, QTableWidget, qVersion
app =QApplication([])
print(qVersion())
qtw = QTableWidget()
qtw.show()
qtw.setColumnCount(8)
qtw.setRowCount(7)
app.exec_()
With PyQt4, use this import (also remove the print(qVersion()) line):
from PyQt4.QtGui import QApplication, QTableWidget
My method:
class MyDelegate(QItemDelegate):
def setEditorData(self,editor,index):
editor.setAutoFillBackground(True)
editor.setText(index.data().toString())
Generally, edit behavior is controlled via QItemDelegates. Typically, this is done to provide more advanced editing, or to filter input data or perform some side effects (like update a database) when edits are made. But you can also use it to just clear the editor presented to the user when editing.
class MyDelegate(QItemDelegate):
def setEditorData(self, editor, index):
# Normally, this would set the text of the editor to the current
# value of the cell. If you do nothing here, it will be blank.
editor.clear()
qtw = QTableWidget()
delegate = MyDelegate(qtw)
qtw.setItemDelegate(delegate)
In my case, above problem comes when I set background color of QWidgetTable to transparent. When I remove the setting, there is no old data overlays the new one anymore.
Hope it helps.
You could try connecting the signal emited by QTableWidget cellClicked(int row, int column) with a slot created for clearing the entry. http://pyqt.sourceforge.net/Docs/PyQt4/new_style_signals_slots.html#connecting-disconnecting-and-emitting-signals
I haven't used Matplotlib much. Based on someone's advice, I'm trying to write some plotting codes using object-oriented paradigms as much as possible---and therefore trying to use pure Matplotlib (i.e. not relying on pyplot) to generate some simple figures.
A stripped-down version of my code looks like this:
import matplotlib as mpl
time = [0,1,2,3,4]
cell = [1,2,1,2,1]
sample = [3,2,3,4,4]
(figHt, figWd) = (5, 8) # in
lBorderWidth = bBorderWidth = rBorderWidth = tBorderWidth = 0.1
lbwh = (lBorderWidth, bBorderWidth,
(1-lBorderWidth-rBorderWidth),
(1-tBorderWidth-bBorderWidth)) # left, bottom, width, height
fig = mpl.figure.Figure(figsize=(figHt, figWd))
ax = fig.add_axes(lbwh)
lines1, = ax.plot(time,cell,'k--')
lines2, = ax.plot(time,sample,'k-')
fig.legend([lines1,lines2],['p','q'],'upper left')
fig.canvas.draw()
But when I run it, Python complains when it reaches fig.canvas.draw() that canvas type is None.
Based on a reading of the Matplotlib Artists tutorial, it seems like pyplot takes care of a few behind-the-scenes setup tasks, most notably establishing the connection between the Figure object and the desired renderer/backend. The tutorial says:
In the example below, we create a Figure instance using matplotlib.pyplot.figure(), which is a convenience method for
instantiating Figure instances and connecting them with your user
interface or drawing toolkit FigureCanvas. As we will discuss below,
this is not necessary – you can work directly with PostScript, PDF
Gtk+, or wxPython FigureCanvas instances, instantiate your Figures
directly and connect them yourselves – but since we are focusing here
on the Artist API we’ll let pyplot handle some of those details for us
Unfortunately, that particular page doesn't proceed beyond generating plots with pyplot.figure(), so I am still trying to discover what the required steps are. Again, I realize pyplot can simplify this task---just trying to grok how all the pieces fit together.
I saw this description of a base class used by backends, FigureCanvasBase, and I assume that I need to assign fig.canvas one of FigureCanvasBase's subclasses.
Also, I verified that Python is using a default backend. So I know the problem isn't caused by lack of a backend.
>>> matplotlib.backends.backend
'Qt4Agg'
Thanks in advance for any help. In summary, two questions:
What am I missing that is causing this to fail? Is it because I didn't assign the figure object a renderer?
I mentioned that I suspected I needed a subclass of FigureCanvasBase to move forward. Even if the problem can probably be solved more elegantly, is there a way to search the Python environment for subclasses that inherit from FigureCanvasBase? This might come in handy in other problems.
You need to create a FigureCanvasAgg in order to plot manually, try this:
import matplotlib as mpl
mpl.use('Agg') #setup the backend
import matplotlib.figure as mfigure
from matplotlib.backends.backend_agg import FigureCanvasAgg #canvas
time = [0,1,2,3,4]
cell = [1,2,1,2,1]
sample = [3,2,3,4,4]
(figHt, figWd) = (5, 8) # in
lBorderWidth = bBorderWidth = rBorderWidth = tBorderWidth = 0.1
lbwh = (lBorderWidth, bBorderWidth,
(1-lBorderWidth-rBorderWidth),
(1-tBorderWidth-bBorderWidth)) # left, bottom, width, height
fig = mfigure.Figure(figsize=(figHt, figWd))
canvas = FigureCanvasAgg(fig) #create the canvas
ax = fig.add_axes(lbwh)
lines1, = ax.plot(time,cell,'k--')
lines2, = ax.plot(time,sample,'k-')
fig.legend([lines1,lines2],['p','q'],'upper left')
fig.savefig('test.png') #save the figure
Note: You can find the subclasses of FigureCanvasBase in matplotlib.backends.<your backend>.FigureCanvas<your backend>
I have been trying for many days to figure out a way to create a transparent Qtextedit with opaque text. Because the term "transparency" is often ambiguous, I define Qtextedit"transparency" as being able to see the text in the Qtextedit overlaid upon whatever is directly behind the main window (such as the desktop background, windows media player etc.) If possible I would like to be able to set the transparency at various levels and cross system compatible, but this is not required.
I am an extreme beginner, as I have only been using pyqt4 for 3 weeks and python 3.x for a few months and this is all the experience with programming that I have obtained in my existence. I have been attempting to decipher the Pyqt documentation with regard to this matter, but it is written in a way that seems to assume that one has been a gui programer for decades, not to mention having knowlege of C++. Furthermore, when this question is asked online it never seems to be resolved in way that is either: a) well documented or b) generalizable
This is very surprising because it seems like a basic operation that people would want to do
This solution works but doesn't seem to be directly useful for anything but displaying transparent images. I also don't really understand it all that well, as simply changing the base class from QWidget to QMainWindow makes the whole thing fail
http://www.loopbacking.info/blog/2008/07/11/transparent-windows-howto/
The following link embodies the common ways people suggest to solve problems similar to this, their pitfalls and why they don't work, but unfortunately they use the C++ version of Qt and are also a bit advanced for my skills at this point.
http://www.qtcentre.org/threads/18072-How-to-set-Qt-window-transparent
My system is windows 7 ultimate 32 bit on a dell latitude d830 with a Quadro NVS 140 whose driver version is current as of this post (Verde 275.33) My version of Pyqt is 4.8 (PyQt-Py3.2-x86-gpl-4.8.5-1.exe Windows 32 bit installer) I am also using Python 3.2.1 (Open Source version)
A basic example of my code lies beneath with the relevant (and failed) lines commented out:
When I tried the commented out code the color I generally just saw blackness. Also, when I resized my windows the darkness would randomly change intensity and the display of the main window seemed to get corrupted when maximized.
I would greatly appreciate any help on this matter!
import sys
import PyQt4
from PyQt4 import QtGui, QtCore
class Transparent(QtGui.QMainWindow):
def __init__(self,parent = None):
QtGui.QMainWindow.__init__(self,parent)
self.initialize()
def initialize(self):
#self.colorset(self,'Window',200,255,100,20)
#self.colorset(self,'Base',200,255,100,20)
#self.setBackgroundRole(QtGui.QPalette.Base)
#self.setAttribute(QtCore.Qt.WA_NoSystemBackground)
#self.setAutoFillBackground(True)
#self.mask()
self.setWindowTitle("Chernobyl-like Failure")
self.answerlabel = QtGui.QLabel('Text Response Display')
self.answerlabel.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Raised)
self.answerlabel.setMinimumHeight(25)
self.questionlabel = QtGui.QLabel("Question:")
self.questionlabel.setFrameStyle(QtGui.QFrame.Panel | QtGui.QFrame.Raised)
self.questionbox = QtGui.QLineEdit()
self.questionbox.setMinimumWidth(500)
self.askbutton = QtGui.QPushButton("Ask it!")
self.historybox = QtGui.QTextEdit('Question & Answer history will be displayed here')
self.historybox.setReadOnly(True)
#self.colorset(self.historybox,'Base',200,255,100,127)
self.grid = QtGui.QGridLayout()
widgetlist = [['answerlabel',0,0,1,3],['questionlabel',1,0,1,1],
['questionbox',1,1,1,1],['askbutton',1,2,1,1],['historybox',2,0,1,3]]
for widget in widgetlist:
self.grid.addWidget(eval("self.{0}".format(widget[0])),*widget[1:])
self.centralwidget = QtGui.QFrame()
self.centralwidget.setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Raised)
self.centralwidget.setLineWidth(5)
self.centralwidget.setLayout(self.grid)
#self.colorset(self.centralwidget,'Base',200,255,100,127)
self.setCentralWidget(self.centralwidget)
def colorset(self,widget,part,h,s,l,a):
pal = widget.palette()
color = QtGui.QColor()
color.setHsl(h,s,l,a)
pal.setColor(eval('QtGui.QPalette.{0}'.format(part)),color)
widget.setPalette(pal)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
main_window = Transparent()
main_window.show()
sys.exit(app.exec_())
To make your main window transparent, you have to set the Qt.WA_TranslucentBackground attribute (using setAttribute(Qt.WA_TranslucentBackground)). Under Windows, you also must set the Qt.FramelessWindowHint attribute on your main window. According to the docs, however, "The user cannot move or resize a borderless window via the window system." So, if you want that functionality, you have to implement it manually. Here is a thread giving an example of that in C++.
Once you have a transparent MainWindow you can control the opacity of it and any child widgets by setting the background color to an RGBA value. Here is a dumb example,
from PyQt4 import QtGui, QtCore
import sys
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
frame = QtGui.QFrame(parent=self)
frame.setStyleSheet("QFrame {background: rgba(0,255,0,20%)}")
box=QtGui.QHBoxLayout()
edit = QtGui.QTextEdit()
edit.setStyleSheet("background: rgba(0,0,255,20%)")
box.addWidget(edit)
edit2=QtGui.QTextEdit()
edit2.setStyleSheet("background: rgb(255,0,0)")
box.addWidget(edit2)
frame.setLayout(box)
pushbutton = QtGui.QPushButton('Quit')
pushbutton.clicked.connect(self.close)
box.addWidget(pushbutton)
self.setCentralWidget(frame)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Main()
main.show()
app.exec_()