I'm writing a program in python 2.7 with PyQt4 and i'm having the following problem with the scrollArea widget. As seen in the images below, the scrollbar is not working correctly. Its not moving the graph at all. The scrollbar in the middle is showing the same thing as in the original position.
scroll bar at original position
scroll bar almost in the middle
I've already try the program in others computers with differents SO and got the same result, which means the problem is the code. The program is too long so i'll just show where the scroll area widget is being called.
class graphWidget(QtGui.QWidget):
def __init__(self):
super(graphWidget, self).__init__()
self.graph=CreateGraphFromInputs.createGraph()
self.slider=QtGui.QSlider(QtCore.Qt.Vertical)
self.setup()
def setup(self):
#slider setup#
self.slider.setMinimum(1)
self.slider.setMaximum(20)
self.slider.setValue(1)
self.slider.setTickPosition(QtGui.QSlider.TicksBelow)
self.slider.setTickInterval(1)
self.slider.valueChanged.connect(self.sliderValueChange)
self.zoomLabel=QtGui.QLabel(str(self.slider.value())+"x")
grid=QtGui.QGridLayout()
grid.addWidget(QtGui.QLabel(""),0,0)
grid.addWidget(QtGui.QLabel("Zoom"),1,1)
grid.addWidget(self.slider,2,1)
grid.addWidget(self.zoomLabel,3,1)
grid.addWidget(QtGui.QLabel(""),4,0)
grid.setRowMinimumHeight(0,55)
grid.setRowMinimumHeight(4,55)
grid.setSpacing(0)
#slider setup#
#scrollArea setup#
scrollArea=QtGui.QScrollArea()
scrollArea.setWidgetResizable(False)
scrollArea.setWidget(self.graph)
scrollArea.setMaximumHeight(270)
scrollArea.setMinimumWidth(600)
#scrollarea setup#
#layout setup
hbox = QtGui.QHBoxLayout()
hbox.addStretch(0)
hbox.setSpacing(5)
hbox.addWidget(scrollArea)
hbox.addLayout(grid)
self.setLayout(hbox)
#layout setup#
def sliderValueChange(self):
if self.graph.lista!=[]:
self.graph.setSize(50+(len(self.graph.lista)/float(self.slider.value())))
self.graph.setSizeDivision(1/float(self.slider.value()))
self.graph.setZoom(50*self.slider.value())
self.zoomLabel.setText(str(self.slider.value())+"x")
self.graph.repaint()
The graph widget is resizable but if i set the line bellow to true, the scroll bar won't work at all.
scrollArea.setWidgetResizable(False)
I've already checked others questions with this problem but did not work for me. So, setting the widget resizable to false makes the scroll bar work, except for the related problem i'm having.
The following code can reproduce the scroll area problem mentioned.
from PyQt4 import QtGui
import sys
import GraphWidget
#graph data
graphList=[1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0]*50
def main():
app = QtGui.QApplication(sys.argv)
graphW=GraphWidget.graphWidget()
graphW.graph.setLista(graphList)
graphW.graph.setSize(50+len(graphList))
graphW.graph.repaint()
graphW.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The codes and the file in theses links below are needed to make it work.
CreateGraphFromInputs.py
DrawGraph.py
ListCleaning.py
ConfigGraphFile.py
graphWidget.py
configGraphFile
Just found out the solution. The problem was that the graph was not being resized correctly. In the DrawGraph.py file, i was setting the geometry instead of resizing it.
So, i just changed this code line
self.setGeometry(0,0,self.size,self.height)
to this one
self.resize(self.size,self.height)
and now its working fine.
Related
python 3.10, 3.11, windows 10
short:
transparency is affecting title bar when it shouldn't, simple code with example below, move the window to the middle of the screen, maximise and restore to reproduce the behaviour
long:
I know it looks so simple, but please bear with me. This thing is driving me nuts. I'm not sure what I could be doing wrong, it looks like a bug, maybe?
Maximising (or restoring to normal state after maximise) breaks the title bar. Title bar is not registering mouse clicks, I can't close the window or resize it because click goes through. It acts as it if it was transparent. In the main app I am using another hidden window with -alpha to grab mouse events on transparent canvas. Both windows are bound together and act as one. Having second window behind this one also doesn't help. Both windows are affected and unclickable (well, -alpha part of the window is clickable, but not the title bar).
I made a very short code to reproduce this behaviour. You can try it with frame instead of a canvas, or a button. Result is the same. Curious thing I have also discovered when using #000000 for colour - it breaks the window in even weirder way. Not sure what to make of it. #000001 works the same as yellow (or blue, red, etc.).
example:
from tkinter import Canvas, Tk
root = Tk()
root.attributes('-transparentcolor', 'yellow', '-topmost', True)
root.geometry("600x600")
x = Canvas(root, bg='yellow')
x.pack(expand=1, fill='both')
root.mainloop()
If I try different width and height values, sometimes I can grab only half of the title bar or close the window, but not minimise. It seems to depend on where the window is on the screen and the size of the object. Feels like the transparent part of the object is extending through the title bar making it unresponsive. I tried separating the title bar from the rest of the window with frames or shapes but it doesn't help (sometimes it works, but is dependent on size and location of the window - you may get lucky and not notice the behaviour)
x = Canvas(root, bg='yellow', height=600, width=600)
x.pack()
The best solution I've come up with so far:
def refresh(self):
self.state('iconic')
if self.state() == 'iconic':
self.state('normal')
self.focus_force() # needed for Entry widget
This function assigned to the button which is minimising the window to the taskbar and then returning it to normal state immediately. Obviously this is far from elegant, because the user have to perform an unnecessary action. I could use overrideredirect and hopefully recreate resize and close functionality of the window but it seems like an overkill for rather simple app.
Not sure what else to say. It's late, bye
edit:
trying this now and it somehow works, but sometimes the window blinks uncomfortably.
from tkinter import Canvas, Tk
class Window(Tk):
def __init__(self):
super().__init__()
self.bind("<Map>", self.refresh)
self.canv = Canvas(self)
self.canv.pack(expand=1, fill='both')
def refresh(self, event):
if self.state() == 'normal':
self.attributes('-transparentcolor', 'yellow', '-topmost', True)
self.canv.configure(bg='yellow')
elif self.state() == 'zoomed':
self.attributes('-transparentcolor', 'blue', '-topmost', True)
self.canv.configure(bg='blue')
if __name__ == '__main__':
w = Window()
w.mainloop()
I'm trying to take a screenshot of the current active window in PyQt5. I know the generic method to take an screenshot of any window is QScreen::grabWindow(winID), for which winID is an implementation-specific ID depending on the window system. Since I'm running X and KDE, I plan to eventual use CTypes to call Xlib, but for now, I simply execute "xdotool getactivewindow" to obtain the windowID in a shell.
For a minimum exmaple, I created a QMainWindow with a QTimer. When the timer is fired, I identify the active window ID by executing "xdotool getactivewindow", get its return value, call grabWindow() to capture the active window, and display the screetshot in a QLabel. On startup, I also set my window a fixed 500x500 size for observation, and activate Qt.WindowStaysOnTopHint flag, so that my window is still visible when it's not in focus. To put them together, the implementation is the following code.
from PyQt5 import QtCore, QtGui, QtWidgets
import subprocess
class ScreenCapture(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
self.setFixedHeight(500)
self.setFixedWidth(500)
self.label = QtWidgets.QLabel(self)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(500)
self.timer.timeout.connect(self.timer_handler)
self.timer.start()
self.screen = QtWidgets.QApplication.primaryScreen()
#QtCore.pyqtSlot()
def timer_handler(self):
window = int(subprocess.check_output(["xdotool", "getactivewindow"]).decode("ascii"))
self.screenshot = self.screen.grabWindow(window)
self.label.setPixmap(self.screenshot)
self.label.setFixedSize(self.screenshot.size())
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = ScreenCapture()
window.show()
app.exec()
To test the implementation, I started the script and clicked another window. It appears to work without problems if there is no overlap between my application window and the active window. See the following screenshot, when Firefox (right) is selected, my application is able to capture the active window of Firefox and display it in the QLabel.
However, the screenshot doesn't work as expected if there is an overlap between the application window and the active window. The window of the application itself will be captured, and creates a positive feedback.
If there is an overlap between the application window and the active window. The window of the application itself will be captured, and creates a positive feedback.
I've already disabled the 3D composite in KDE's settings, but the problem remains. The examples above are taken with all composite effects disabled.
Question
Why isn't this implementation working correctly when the application window and the active window are overlapped? I suspect it's an issue caused by some forms of unwanted interaction between graphics systems (Qt toolkit, window manager, X, etc), but I'm not sure.
Is it even possible solve this problem? (Note: I know I can hide() before the screenshot and show() it again, but it doesn't really solve this problem, which is taking a screenshot even if an overlap exists.)
As pointed out by #eyllanesc, it appears that it is not possible to do it in Qt, at least not with QScreen::grabWindow, because grabWindow() doesn't actually grab the window itself, but merely the area occupied by the window. The documentation contains the following warning.
The grabWindow() function grabs pixels from the screen, not from the window, i.e. if there is another window partially or entirely over the one you grab, you get pixels from the overlying window, too. The mouse cursor is generally not grabbed.
The conclusion is that it's impossible do to it in pure Qt. It's only possible to implement such a functionality by writing a low-level X program. Since the question asks for a solution "in Qt", any answer that potentially involves deeper, low-level X solutions are out-of-scope. This question can be marked as resolved.
The lesson to learn here: Always check the documentation before using a function or method.
Update: I managed to solve the problem by reading the window directly from X via Xlib. Somewhat ironically, my solution uses GTK to grab the window and sends its result to Qt... Anyway, you can write the same program with Xlib directly if you don't want to use GTK, but I used GTK since the Xlib-related functions in GDK is pretty convenient to demonstrate the basic concept.
To get a screenshot, we first convert our window ID to an GdkWindow suitable for use within GDK, and we call Gdk.pixbuf_get_from_window() to grab the window and store it in a gdk_pixbuf. Finally, we call save_to_bufferv() to convert the raw pixbuf to a suitable image format and store it in a buffer. At this point, the image in the buffer is suitable to use in any program, including Qt.
The documentation contains the following warning:
If the window is off the screen, then there is no image data in the obscured/offscreen regions to be placed in the pixbuf. The contents of portions of the pixbuf corresponding to the offscreen region are undefined.
If the window you’re obtaining data from is partially obscured by other windows, then the contents of the pixbuf areas corresponding to the obscured regions are undefined.
If the window is not mapped (typically because it’s iconified/minimized or not on the current workspace), then NULL will be returned.
If memory can’t be allocated for the return value, NULL will be returned instead.
It also has some remarks about compositing,
gdk_display_supports_composite has been deprecated since version 3.16 and should not be used in newly-written code.
Compositing is an outdated technology that only ever worked on X11.
So basically, it's only possible to grab a partially obscured window under X11 (not possible in Wayland!), with a compositing window manager. I tested it without compositing, and found the window is blacked-out when compositing is disabled. But when composition is enabled, it seems to work without problem. It may or may not work for your application. But I think if you are using compositing under X11, it probably will work.
from PyQt5 import QtCore, QtGui, QtWidgets
import subprocess
class ScreenCapture(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
self.setFixedHeight(500)
self.setFixedWidth(500)
self.label = QtWidgets.QLabel(self)
self.screen = QtWidgets.QApplication.primaryScreen()
self.timer = QtCore.QTimer(self)
self.timer.setInterval(500)
self.timer.timeout.connect(self.timer_handler)
self.timer.start()
#staticmethod
def grab_screenshot():
from gi.repository import Gdk, GdkX11
window_id = int(subprocess.check_output(["xdotool", "getactivewindow"]).decode("ascii"))
display = GdkX11.X11Display.get_default()
window = GdkX11.X11Window.foreign_new_for_display(display, window_id)
x, y, width, height = window.get_geometry()
pb = Gdk.pixbuf_get_from_window(window, 0, 0, width, height)
if pb:
buf = pb.save_to_bufferv("bmp", (), ())
return buf[1]
else:
return
#QtCore.pyqtSlot()
def timer_handler(self):
screenshot = self.grab_screenshot()
self.pixmap = QtGui.QPixmap()
if not self.pixmap:
return
self.pixmap.loadFromData(screenshot)
self.label.setPixmap(self.pixmap)
self.label.setFixedSize(self.pixmap.size())
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = ScreenCapture()
window.show()
app.exec()
Now it captures an active window perfectly, even if there are overlapping windows on top of it.
I use python2.7 and PyQt4. I created a simple app with a button-box and a table-widget. If I edit a table cell and press the Ok button, the cell editor always disappears. But after I add the app.setStyleSheet(s) line, the cell editor does not disappear after the OK button is pressed. What is going on?
import sys
from PyQt4 import QtGui
class Widget(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
l = QtGui.QVBoxLayout(self)
table = QtGui.QTableWidget()
table.setColumnCount(3)
table.setRowCount(5)
l.addWidget(table)
l.addWidget(QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
s = "QWidget{background:red;}"
# app.setStyleSheet(s)
app.setStyleSheet(s)
mw = QtGui.QMainWindow()
w = Widget()
mw.setCentralWidget(w)
mw.show()
sys.exit(app.exec_())
This seems to be a bug in Qt4, but I can't find a specific report for it. The same code works as expected when using PyQt5, so it somehow got fixed at some point.
The bug is actually in QDialogButtonBox rather than QTableWidget. If you add a QLineEdit to the example and set focus on it, you will see that clicking on the Ok button does not switch focus. Or to be more precise, clicking on the first button does not switch focus. All the other buttons in the button-box work normally.
I thought that this might have something to do with the default and/or autoDefault properties, because they usually change the way buttons are styled (e.g. a highlighted border). But setting the properties doesn't have any effect - the bug really does seem to affect just the first button.
I am building an Interface with TKinter and I have the problem, that whenever create a new window the standardized tkinter window of 200x200 pixel with nothing in it flashes up for a fraction of a second and AFTER that all my modifications (widgets ect.) are made. This happens before AND after calling the mainloop.
The Main-Interface is created.
Mainloop stats
Flashing window
Main-Interface appears
Also after Mainloop was called this happens with newly created windows.
Main-Interface appears--> Push a button, that creates a new window
Flashing window
new window appears
Sadly I cannot give you a sample code... If I try to do a minimal example, this doesn't happen. Maybe the standard window is created, but it is changed so fast, that it doesn't appear on screen. I don't even know what to look up in this case... searching "tkinter flashing window" yields nothing.
EDIT: I found the source of the problem. It seems to be caused by wm_iconbitmap, FigureCanvasTkAgg and tkinter.Toplevel. If you remove the the icon out of the code, it works fine, no flashing. But if I use it together with one of the other, the window flashes when created. Try it out with the code below. You have to put the icon in the working directory of course.
Here is a code sample and the link to the icon I am using, but I suppose any icon will do.
# coding=utf-8
import numpy as np
import matplotlib as mpl
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import os
class INTERFACE(object):
def __init__(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.EXIT)
self.root.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
self.root.geometry("1024x768")
canvas = FigureCanvasTkAgg(self.testfigure(), master=self.root) #<---- !!!!!!
canvas.get_tk_widget().grid(sticky=tk.N+tk.W+tk.E+tk.S)
self.root.rowconfigure(0, weight=1)
self.root.columnconfigure(0, weight=1)
def testfigure(self):
x=np.linspace(0, 2*np.pi,100)
y=np.sin(x)
fig = mpl.figure.Figure()
sub = fig.add_subplot(111)
sub.plot(x,y)
return fig
def EXIT(self):
Top = tk.Toplevel(master=self.root)
Top.wm_iconbitmap( os.path.abspath("icon.ico")) #<---- !!!!!!
Top.transient(self.root)
Top.resizable(width=False, height=False)
Top.title("Exit")
tk.Message(Top,text="Do you really want to quit?", justify=tk.CENTER, width=300).grid(row=0,columnspan=3)
tk.Button(Top,text="YES",command=self.root.destroy).grid(row=1,column=0)
tk.Button(Top,text="No",command=self.root.destroy).grid(row=1,column=1)
tk.Button(Top,text="Maybe",command=self.root.destroy).grid(row=1,column=2)
def start(self):
self.root.mainloop()
if __name__ == '__main__':
INTERFACE().start()
I know this is an old question, but I've experienced a similar situation and have found a solution.
In my case, I've isolated the issue to the use of iconbitmap. I've managed to solve it by calling iconbitmap with the after method just before calling root.mainloop().
Example:
from tkinter import *
root = Tk()
w = Label(root, text="Hello, world!")
w.pack()
root.geometry('300x300+500+500')
root.after(50, root.iconbitmap('icon.ico'))
root.mainloop()
This method has worked on Toplevel() windows with icons as well.
Tested on Win 8.1 with Python 3.5.0.
Edit: Upon further inspection I've noticed that the behavior changes relative to root.geometry's presence as well. My initial example didn't have it and I only noticed after a few tries that it still had the same issue. The time delay in the after method doesn't seem to change anything.
Moving root.geometry below the after method yields the same issue for some reason.
Most likely, somewhere in your initialization code you're calling update or update_idletasks, which causes the current state of the GUI to be drawn on the screen.
Another possible source of the problem is if you're creating multiple instances of Tk rather than Toplevel.
Without seeing your code, though, all we can do is guess.
Your best route to solving this problem is to create a small example that has the same behavior. Not because we need it to help you, but because the effort you put into recreating the bug will likely teach you what is causing the bug.
This should work but it requires win32gui
import win32gui
def FlashMyWindow(title):
ID = win32gui.FindWindow(None, title)
win32gui.FlashWindow(ID,True)
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_()