I intend to change the monitor where I show a fullscreen window.
This is especially interesting when having a projector hooked up.
I've tried to use fullscreen_on_monitor but that doesn't produce any visible changes.
Here is a non-working example:
#!/usr/bin/env python
import sys
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from gi.repository import Gdk
w = Gtk.Window()
screen = Gdk.Screen.get_default()
print ("Montors: %d" % screen.get_n_monitors())
if len(sys.argv) > 1:
n = int(sys.argv[1])
else:
n = 0
l = Gtk.Button(label="Hello, %d monitors!" % screen.get_n_monitors())
w.add(l)
w.show_all()
w.fullscreen_on_monitor(screen, n)
l.connect("clicked", Gtk.main_quit)
w.connect("destroy", Gtk.main_quit)
Gtk.main()
I get to see the window on the very same monitor (out of 3), regardless of the value I provide.
My question is: how do I make the fullscreen window appear on a different monitor?
The problem seems to be that Gtk just ignores the monitor number, it will always fullscreen the window on the monitor on which the window currently is positioned. This sucks, but we can use that to make it work the way we want to.
But first some theory about multiple monitors, they aren't actually separate monitors for your pc. It considers them to collectively form one screen which share the same global origin. On that global screen each monitor has a origin relative to the global origin, just like windows.
Because we know that Gtk will always fullscreen on the monitor on which the window is we can simply move the window to the origin of the monitor using window.move(x,y) and then call window.fullscreen().
(The move function will move the window to a position (x,y) relative to it's parent, which in the case of the main window is the global screen.)
Combining all this we get this, which works perfectly on Windows 10:
def fullscreen_at_monitor(window, n):
screen = Gdk.Screen.get_default()
monitor_n_geo = screen.get_monitor_geometry(n)
x = monitor_n_geo.x
y = monitor_n_geo.y
window.move(x,y)
window.fullscreen()
Here is an updated version of #B8vrede's answer, because get_monitor_geometry is deprecated since 3.22.
def fullscreen_at_monitor(window, n):
display = Gdk.Display.get_default()
monitor = Gdk.Display.get_monitor(display, n)
geometry = monitor.get_geometry()
x = geometry.x
y = geometry.y
window.move(x,y)
window.fullscreen()
Related
I have developed a controller for RGB LEDs on the back of my monitor, and I would like to control them so that they match the average color on the screen when I have a full screen app running, such as a movie.
I already have the whole controller up and running in the background, but I got stuck trying to figure out how to determine if there is some app running full-screen or not. How could i do it? I am using python3 on Debian testing.
Thanks a lot for any help!
I found an answer here and modified it a bit to make it more usable. Here is my code that works on gnome. You might have to adjust the escaped windows names for other gdms.
import Xlib.display
#Find out if fullscreen app is running
screen = Xlib.display.Display().screen()
root_win = screen.root
def is_fullscreen():
#cycle through all windows
for window in root_win.query_tree()._data['children']:
width = window.get_geometry()._data["width"]
height = window.get_geometry()._data["height"]
#if window is full screen, check it the window name
if width == screen.width_in_pixels and height == screen.height_in_pixels:
if window.get_wm_name() in ['Media viewer', 'mutter guard window']:
continue
#return true if window name is not one of the gnome windows
return True
#if we reach this, no fs window is open
return False
I am using pygame to program a simple behavioral test. I'm running it on my macbook pro and have almost all the functionality working. However, during testing I'll have a second, external monitor that the subject sees and the laptop monitor. I'd like to have the game so up fullscreen on the external monitor and not on the laptop's monitor so that I can monitor performance. Currently, the start of the file looks something like:
#! /usr/bin/env python2.6
import pygame
import sys
stdscr = curses.initscr()
pygame.init()
screen = pygame.display.set_mode((1900, 1100), pygame.RESIZABLE)
I was thinking of starting the game in a resizable screen, but that OS X has problems resizing the window.
Pygame doesn't support two displays in a single pygame process(yet). See the question here and developer answer immediately after, where he says
Once SDL 1.3 is finished then pygame will get support for using multiple windows in the same process.
So, your options are:
Use multiple processes. Two pygame instances, each maximized on its own screen, communicating back and forth (you could use any of: the very cool python multiprocessing module, local TCP, pipes, writing/reading files, etc)
Set the same resolution on both of your displays, and create a large (wide) window that spans them with your information on one half and the user display on the other. Then manually place the window so that the user side is on their screen and yours is on the laptop screen. It's hacky, but might a better use of your time than engineering a better solution ("If it's studpid and it works, it ain't stupid" ;).
Use pyglet, which is similar to pygame and supports full screen windows: pyglet.window.Window(fullscreen=True, screens[1])
Good luck.
I do not know if you can do this in OS X, but this is worth mentioning for the Windows users out there, if you just want to have your program to run full screen on the second screen and you are on windows, just set the other screen as the main one.
The setting can be found under Rearrange Your Displays in settings.
So far for me anything that I can run on my main display can run this way, no need to change your code.
I did something silly but it works.
i get the number of monitors with get_monitors()
than i use SDL to change the pygame window's display position by adding to it the width of the smallest screen, to be sure that the window will be positionned in the second monitor.
from screeninfo import get_monitors
numberOfmonitors = 0
smallScreenWidth = 9999
for monitor in get_monitors():
#getting the smallest screen width
smallScreenWidth = min(smallScreenWidth, monitor.width)
numberOfmonitors += 1
if numberOfmonitors > 1:
x = smallScreenWidth
y = 0
#this will position the pygame window in the second monitor
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
#you can check with a small window
#screen = pygame.display.set_mode((100,100))
#or go full screen in second monitor
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
#if you want to do other tasks on the laptop (first monitor) while the pygame window is being displayed on the second monitor, you shoudn't use fullscreen but instead get the second monitor's width and heigh using monitor.width and monitor.height, and set the display mode like
screen = pygame.display.set_mode((width,height))
display = pyglet.canvas.get_display()
display = display.get_screens()
win = pyglet.window.Window(screen=display[1])
------------------------------------------------------
screen=display[Номер монитора]
------------------------------------------------------
display = pyglet.canvas.get_display()
display = display.get_screens()
print(display) # Все мониторы которые есть
Important note: I'm using PyGObject to get access to the GTK widgets, not PyGTK. That's what makes this question different from similar ones:
PyGTK: How do I make an image automatically scale to fit it's parent widget?
Scale an image in GTK
I want to make a very simple app that displays a label, an image and a button, all stacked on top of each other. The app should be running in fullscreen mode.
When I attempted it, I've run into a problem. My image is of very high resolution, so when I simply create it from file and add it, I can barely see 20% of it.
What I want is for this image to be scaled by width according to the size of the window (which is equal to the screen size as the app runs in fullscreen).
I've tried using Pixbufs, but calling scale_simple on them didn't seem to change much.
Here's my current code:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf
class Window(Gtk.Window):
def __init__(self):
super().__init__(title='My app')
layout = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
dimensions = layout.get_allocation()
pixbuf = GdkPixbuf.Pixbuf.new_from_file('path/to/image')
pixbuf.scale_simple(dimensions.width, dimensions.height, GdkPixbuf.InterpType.BILINEAR)
image = Gtk.Image.new_from_pixbuf(pixbuf)
dismiss_btn = Gtk.Button(label='Button')
dismiss_btn.connect('clicked', Gtk.main_quit)
layout.add(image)
layout.add(dismiss_btn)
self.add(layout)
win = Window()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
The problem is that scale_simple actually returns a new Pixbuf, as per GTK+ docs.
Getting screen dimensions can be done by calling .get_screen() on the window and then calling .width() or .height() on the screen object.
The whole code put together looks something like this:
screen = self.get_screen()
pixbuf = GdkPixbuf.Pixbuf.new_from_file('/path/to/image')
pixbuf = pixbuf.scale_simple(screen.width(), screen.height() * 0.9, GdkPixbuf.InterpType.BILINEAR)
image = Gtk.Image.new_from_pixbuf(pixbuf)
I'm using gtk.Table in combination with an EventBox for every cell to draw a colored grid. After trying out a minimal example i've discovered that the window is twice as big as the actual table. Also it's not possible to shrink the window any further when the application is running.
It seems like something went horrible wrong but i'm not able to figure out the cause. Here's the minimal example to reproduce the misbehaviour:
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class MyProgram:
def __init__(self):
app_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
app_window.set_border_width(10)
app_window.connect("delete_event", lambda w,e: gtk.main_quit())
vbox_app = gtk.VBox(False, 0)
table_layout = gtk.Table(rows=1,columns=1, homogeneous=True)
for col in range(1,10):
for row in range(1,5):
event_box = gtk.EventBox()
label_day = gtk.Label("")
label_day.set_size_request(18,18)
label_day.show()
event_box.add(label_day)
event_box.modify_bg(gtk.STATE_NORMAL,
event_box.get_colormap().alloc_color("orange"))
event_box.set_border_width(25)
event_box.show()
table_layout.attach(event_box, 0, col, 0, row, 0,0,0,0)
vbox_app.pack_start(table_layout)
app_window.add(vbox_app)
app_window.show_all()
return
def main():
gtk.main()
return 0
if __name__ == "__main__":
MyProgram()
main()
It turns out it's your event_box.set_border_width(25) that hurts. If you just want to space evenly your labels, us the padding arguments of GtkTable::attach instead.
Here are unrelated improvements:
don't connect gtk_main_quit to the delete-event signal, connect it to the destroy signal instead. delete-event is when you want to do something before quitting (for example, display a popup "are you sure ? yes/no"), but what you want is to quit gtk when the window is really destroyed.
Also, instead of prepending your widgets when adding them in the table, append them and use a range starting from 0 so it's easier to see where the widgets are added (the indexes in the table are zero-based).
As for your VBox, in this context it's useless. If you only have widget which content takes the whole window, just add it directly to your GtkWindow (but maybe it's needed in our unstripped version of the program).
Finally, you don't need to call gtk_widget_show on each widget. Just focus on constructing your widget hierarchy, and then run gtk_widget_show_all on the toplevel window. It will recursively show all the widgets in it.
That gives us in the end:
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
class MyProgram:
def __init__(self):
app_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
app_window.set_border_width(10)
app_window.connect('destroy', lambda w: gtk.main_quit())
table = gtk.Table(rows=1, columns=1, homogeneous=True)
for col in range(0,9):
for row in range(0,4):
event_box = gtk.EventBox()
label_day = gtk.Label('')
label_day.set_size_request(18, 18)
event_box.add(label_day)
event_box.modify_bg(gtk.STATE_NORMAL,
event_box.get_colormap().alloc_color("orange"))
# event_box.set_border_width(25)
table.attach(event_box, col, col + 1, row, row + 1, 0, 0, 12, 12)
app_window.add(table)
app_window.show_all()
if __name__ == '__main__':
MyProgram()
gtk.main()
You also have a tool named gtk-inspector but I don't know if it works with GTK 2. If not, fallback on gtk-parasite. These tools will help you analyze a running gtk user interface and see the characteristics of its widgets.
Also, GTK 3 has been out for a few years now and GTK 4 is on the road. Consider using GTK 3 for new code. It doesn't use pygtk anymore, it's pygobject instead trough the gi package (GObject introspection).
Here's the GTK 3 in python official tutorial:
https://python-gtk-3-tutorial.readthedocs.io/en/latest/index.html
I want to change the position of Game window with respect to Computer Screen. But couldn't find anything in Documentation. Please Help me.
On windows, it is possible to change the position of an initialized window using its handle (hwnd). In User32.dll there is a function called MoveWindow, that recieves a window's handle and changes its position. You can call it using python's standard ctypes module.
from ctypes import windll
def moveWin(x, y):
# the handle to the window
hwnd = pygame.display.get_wm_info()['window']
# user32.MoveWindow also recieves a new size for the window
w, h = pygame.display.get_surface().get_size()
windll.user32.MoveWindow(hwnd, x, y, w, h, False)
You can set the position that the pygame display initializes with environment variables that can be accessed with os.environ. An example from http://www.pygame.org/wiki/SettingWindowPosition?parent=CookBook:
x = 100
y = 0
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
import pygame
pygame.init()
screen = pygame.display.set_mode((100,100))
# wait for a while to show the window.
import time
time.sleep(2)
I do not think it's possible to change the position of an already initialized display, but you could quit the current display, set the environment variable, and reinitialize it.