I am running a little program in python that launches a small window that needs to stay on top of all the other windows. I believe this is OS specific, how is it done in GNU-Linux with GNOME?
[Update - Solution for Windows]
Lovely, I think I got it working. I am using Python 2.5.4 with Pygame 1.9.1 in Eclipse on Vista 64-bit. Thus, this is for windows systems. The SetWindowPos function is documented Here. I will refer to this in my explanation.
Imports:
from ctypes import windll
Then I set up a variable that calls the "SetWindowPos" in user32:
SetWindowPos = windll.user32.SetWindowPos
Now, let's say I just made a window:
screen = pygame.display.set_mode((100,100), pygame.NOFRAME)
The next line is the key. This sets the window to be on top of other windows.
SetWindowPos(pygame.display.get_wm_info()['window'], -1, x, y, 0, 0, 0x0001)
Basically, You supply the hWnd(Window Handle) with the window ID returned from a call to display.get_wm_info(). Now the function can edit the window you just initialized.
The -1 is our hWndInsertAfter.
The MSDN site says:
A window can be made a topmost window either by setting the hWndInsertAfter parameter to HWND_TOPMOST and ensuring that the SWP_NOZORDER flag is not set, or by setting a window's position in the Z order so that it is above any existing topmost windows. When a non-topmost window is made topmost, its owned windows are also made topmost. Its owners, however, are not changed.
So, the -1 makes sure the window is above any other existing topmost windows, but this may not work in all cases. Maybe a -2 beats a -1? It currently works for me. :)
The x and y specify the new coordinates for the window being set. I wanted the window to stay at its current position when the SetWindowPos function was called on it. Alas, I couldn't find a way to easily pass the current window (x,y) position into the function. I was able to find a work around, but assume I shouldn't introduce a new topic into this question.
The 0, 0, are supposed to specify the new width and height of the window, in pixels. Well, that functionality is already in your pygame.display.set_mode() function, so I left them at 0. The 0x0001 ignores these parameters.
0x0001 corresponds to SWP_NOSIZE and is my only uFlag. A list of all the available uFlags are on the provided documentation page. Some of their Hex representations are as follows:
SWP_NOSIZE = 0x0001
SWP_NOMOVE = 0x0002
SWP_NOZORDER = 0x0004
SWP_NOREDRAW = 0x0008
SWP_NOACTIVATE = 0x0010
SWP_FRAMECHANGED = 0x0020
SWP_SHOWWINDOW = 0x0040
SWP_HIDEWINDOW = 0x0080
SWP_NOCOPYBITS = 0x0100
SWP_NOOWNERZORDER = 0x0200
SWP_NOSENDCHANGING = 0x0400
That should be it! Hope it works for you!
Credit to John Popplewell at john#johnnypops.demon.co.uk for his help.
The question is more like which windowing toolkit are you using ? PyGTK and similar educated googling gave me this:
gtk.Window.set_keep_above
As mentioned previously it is upto the window manager to respect this setting or not.
Edited to include SDL specific stuff
Pygame uses SDL to do display work and apprently does not play nice with Windowing toolkits. SDL Window can be put on top is discussed here.
I really don't know much Python at all, but Googling "pygtk always on top" gave me this:
http://www.mail-archive.com/pygtk#daa.com.au/msg01370.html
The solution posted there was:
transient.set_transient_for(main_window)
You might also want to search things like "x11 always on top". The underlying concept seems to be that you're giving the window manager a "hint" that it should keep the window above the others. The window manager, however, has free reign and can do whatever it wants.
I've also seen the concept of layers when using window managers like Fluxbox, so maybe there's a way to change the layer on which the window appears.
I was trying to figure out a similar issue and found this solution using the Pmw module
http://www.java2s.com/Code/Python/GUI-Pmw/Showglobalmodaldialog.htm
Related
I have a function button_max_slot that use showMaximized to maximize the window, and another function button_restore_slot to restore the window's size and place. the first time I call the button_max_slot it works great, then I use button_restore_function to restore window's size and place. But the second time I call the button_max_slot to maximize the window, it didn't work. I call self.isMaximized() and it returns true, but actually the window doesn't maximized.What should I do to fix this problem?Here is a minimal reproducible example:
from PyQt5 import QtWidgets
import sys
class MyWindow(QtWidgets.QMainWindow):
last_geometry = None
def __init__(self):
super().__init__()
self.resize(960, 540)
self.ButtonMax = QtWidgets.QPushButton(self)
self.ButtonMax.setObjectName("ButtonMax")
self.ButtonMax.setText("Max")
self.ButtonMax.move(100, 100)
self.ButtonRestore = QtWidgets.QPushButton(self)
self.ButtonRestore.setText("Restore")
self.ButtonRestore.setObjectName("ButtonRestore")
self.ButtonRestore.move(300, 100)
self.ButtonRestore.setEnabled(False)
self.ButtonMax.clicked.connect(self.button_max_slot)
self.ButtonRestore.clicked.connect(self.button_restore_slot)
def button_max_slot(self):
self.last_geometry = self.geometry()
self.showMaximized()
self.ButtonRestore.setEnabled(True)
self.ButtonMax.setEnabled(False)
def button_restore_slot(self):
self.setGeometry(self.last_geometry)
self.ButtonRestore.setEnabled(False)
self.ButtonMax.setEnabled(True)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
myshow = MyWindow()
myshow.show()
sys.exit(app.exec_())
tl;dr
Use showNormal() instead of setGeometry() to restore the window state.
Explanation
While not intuitive, it's still possible to set a geometry of a window even if its state should not allow it, and that's because setting the state of a window is not the same as setting its geometry.
Simply put, setting the window state tells the underlying platform/window manager to "choose" the geometry of the window based on the specified state, while setting the geometry asks the system to explicitly set the position and size of the window. Whether the system allows it, is another story.
An important thing to consider is that a QWidget (even a top level one, including a QDialog or a QMainWindow) is not the actual window shown on the screen. What you see is the QWindow (an abstract representation of the system's window for that widget), which is what actually contains the QWidget (or any of its inherited classes). Setting the state acts on the QWindow, while setting the geometry normally acts on the contained widget, excluding the possible window frame.
For instance, I'm able to reproduce your issue on my Linux system, but ekhumoro cannot, even though we both are using Linux (we're both using similar window managers, but they're still different: he's on OpenBox, I'm on FluxBox). Furthermore, I get inconsistent behavior after pressing the "Max" button, even if using the system features.
The fact that you got a maximized window state even if it doesn't look like it is, is exactly related to that: the state is maximized, the geometry isn't (because you changed it).
Consider it the other way around: you can manually resize a window in order to precisely occupy the whole available screen size, but that doesn't make it maximized: you can still probably see the "maximize" button in it's title bar (not the "normalize" one), and maybe even the window borders that are normally hidden when the window is actually maximized.
Note that the inconsistent behavior shown on different OS or window managers relies on two sides: the OS/wm implementation and Qt attempts to use a "standardized" behavior across all systems.
The solution is simple: just restore the state using showNormal() instead of setGeometry().
It usually works on all systems, with the exception of some very specific window managers on linux (and maybe some "non standard" behavior in recent Windows/MacOS versions), but it's the accepted approach.
For those cases, you might consider storing and restoring the geometry by overriding the top level window's changeEvent(), checking if the event.type() is a WindowStateChange and eventually decide the behavior based on the current windowState() and the oldState() of that event.
Remember that window states are flags, so they can be an OR combination of Qt.WindowState enums.
I'm creating a python program that is supposed to streamline the process of setting up a computer. I want this python program to change the screen resolution of the computer and scaling of it. I'm not sure what the best approach is however, or how to approach it.
I've tried using an example pywin32 program, but it only outputted an array of resolution sizes
I had a look how to change screen resolution using C++ and then translated it to Python:
import win32api
import win32con
import pywintypes
devmode = pywintypes.DEVMODEType()
devmode.PelsWidth = 1366
devmode.PelsHeight = 768
devmode.Fields = win32con.DM_PELSWIDTH | win32con.DM_PELSHEIGHT
win32api.ChangeDisplaySettings(devmode, 0)
We needed a DEVMODE object to pass to the ChangeDisplaySettings function. The pywintypes module which is also part of pywin32 has a function to create objects of type DEVMODE.
We then set the PelsWidth and PelsHeight fields and also the Fields field to tell the API which field's values we want to use.
To change back to the previous resolution, simply call:
win32api.ChangeDisplaySettings(None, 0)
Thanks for asking the question. I've learned something.
I noticed that if using windows focus detection, the rect value will change depending on what part of the program you are in. I want the size of the main window only if possible.
I've managed to get around some of these issues by checking the window title. If the title is empty, it's a dropdown menu, so ignore. If it is titled "Open", "Save As", etc, then it's obviously a dialogue box and ignore. However, the message "Do you want to quit without saving?" seems to have only the title of the program itself, so that slips through the check.
I just tested the heights of those boxes to see if there was a constant value to ignore, but each program seems to have different heights. I could possibly just ignore anything under a certain resolution, but I'd prefer not to as it's not actually solving the issue, and there may be some rare cases where it needs to use that particular resolution.
Here's the bits of code I currently use from pywin32 to do the detection. I also have the ctypes alternatives, but it's basically the same thing.
import win32gui
import win32process
import psutil
hwnd = win32gui.GetForegroundWindow()
print 'pid:', win32process.GetWindowThreadProcessId(hwnd)[1]
print 'rect:', win32gui.GetWindowRect(hwnd)
print 'name:', win32gui.GetWindowText(hwnd)
print 'exe:', psutil.Process(win32process.GetWindowThreadProcessId(hwnd)[1]).name()
Basically I want the size of the main window at all times, no matter which other windows are loaded on top of it. I've only found GetWindowRect and GetClientRect which both do the same thing, just wondering if I'm missing something?
Managed to get to the solution by realising it was basically the last parent window I needed:
def _get_parent(self):
while True:
try:
parent = win32gui.GetParent(hwnd)
except UnboundLocalError:
hwnd = win32gui.GetForegroundWindow()
else:
if parent:
hwnd = parent
else:
break
return hwnd
Basically it just keeps checking if the current window has a parent until there are no more parents. Not super efficient but does the job. I've only really tested it on paint > save as > are you sure you want to replace?, but it gets the correct hwnd value for the main window and returns a constant resolution.
I'm coding using Python and OpenCV on Ubuntu 14.04. When I click on the right button of the mouse, the associated mouse event cv2.EVENT_RBUTTONDOWN does not work and I rather get context menu ("actions"). Is there a way to disable the context menu popup?
A user gave me a hint and I am sure the solution is somewhere there. He asked me to add CV_GUI_NORMAL as shown on here.
So I run: cv2.namedWindow("Window",CV_GUI_NORMAL) but I got this error:
NameError: global name 'CV_GUI_NORMAL' is not defined
When I try cv2.CV_GUI_NORMAL as on the below user's comment instead, I get this error:
AttributeError: 'module' object has no attribute 'CV_GUI_NORMAL'
Note that similar question was asked here but I do not want to change OpenCV code.
How to fix this ?
.
You can use cv2.WINDOW_GUI_NORMAL as follows:
cv2.namedWindow('desired_name_of_window', flags= cv2.WINDOW_GUI_NORMAL)
Allowed windows values in cv2 are:
WINDOW_AUTOSIZE = 1
WINDOW_FREERATIO = 256
WINDOW_FULLSCREEN = 1
WINDOW_GUI_EXPANDED = 0
WINDOW_GUI_NORMAL = 16
WINDOW_KEEPRATIO = 0
WINDOW_NORMAL = 0
WINDOW_OPENGL = 4096
WND_PROP_ASPECT_RATIO = 2
WND_PROP_AUTOSIZE = 1
WND_PROP_FULLSCREEN = 0
WND_PROP_OPENGL = 3
WND_PROP_VISIBLE = 4
The official documentation says:
Python:
cv.NamedWindow(name, flags=CV_WINDOW_AUTOSIZE) → None
Parameters:
name – Name of the window in the window caption that may be used as a window identifier.
flags –
Flags of the window. The supported flags are:
WINDOW_NORMAL If this is set, the user can resize the window (no constraint).
WINDOW_AUTOSIZE If this is set, the window size is automatically adjusted to fit the displayed image (see imshow() ), and you cannot change the window size manually.
WINDOW_OPENGL If this is set, the window will be created with OpenGL support.
Only some implementations with Qt backend support CV_GUI_NORMAL. It seems you have no choice than to install cv2 with Qt support or use other variables.
In that case you'll be using cv2.CV_WINDOW_NORMAL.
For a starter you could build without Qt support if you do not need it. It seems to do more harm than good in many cases. So it is better set the flag WINDOW_OPENGL: That way you disable the QT support and get the OpenGL one.
Every result I've gotten from searching this question up has to do with changing the border color for Tkinter widget's WITHIN the main app window, or changing the background color and stuff.
What I'm looking to do is actually change the windows border color itself. Which I have marked in this picture here
I read some stuff concerning 'Styles' but it didn't seem to be what I was looking for.
Can anyone help point me to a tutorial for changing this, or explain how it can be done?
Or if there is a question that asked exactly the same question I was asking that is answered point me to it.
As I see you are using windows.
This color is set by the theme you are currently using. It is the same for every window.
So I cross out the possibility of only using the Tkinter module for this.
Tkinter is responsible for what is in the window but the window manager decides about the border. For example in Ubuntu the window would look totally different.
I guess, you would need some windows specific calls for that.
You can remove the border with root.overrideredirect(1) if I remember correctly.
PS: put "windows" into the tags of this question.
Hi I was looking for this answer too, found it after like 80 minutes searching, Only work, to activate DWMWA_USE_IMMERSIVE_DARK_MODE
found it here: Can I change the title bar in Tkinter?
I didnt found dwmwindowattribute in dwmwindowattribute that affect Top bar color sadly :(.
For border colour:
It should be possible to call DWMWA_BORDER_COLOR, but honestly I dont know how, there is some article calling it in C++ here:
change-the-color-of-the-title-bar-caption-of-a-win32-application
Tried this but doesnt work:
set_window_attribute(hwnd, 22, '0x000000FF', 4)
Here is working code for pure black top:
import tkinter as tk
import ctypes as ct
def dark_title_bar(window):
"""
MORE INFO:
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
"""
window.update()
set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
get_parent = ct.windll.user32.GetParent
hwnd = get_parent(window.winfo_id())
value = 2
value = ct.c_int(value)
set_window_attribute(hwnd, 20, ct.byref(value),
4)
root = tk.Tk()
root.title("Crystaly Ball")
root.geometry("1400x900")
root.configure(background="#222246")
dark_title_bar(root)
root.mainloop()