问题描述
关于如何在屏幕上居中一个 python tkinter 窗口有很多问题,答案很好。我的问题是我所谓的“屏幕”看起来像这样:
虽然您可以将窗口部分(或全部)移动到灰色区域,但它们实际上不会显示在我的三台显示器中的任何一台上。左上显示器为 1920x1080,右上显示器为 3840x2160,右下显示器为 1920x1080。
程序可以通过桌面图标启动,桌面图标可以在任何显示器上,也可以通过 gnome-terminal
启动,gi
可以在任何显示器上。如何发现:
- 调用 python 时哪个监视器处于活动状态?
- 屏幕空间内活动监视器的坐标?
虽然我使用的是 Gnome 桌面,但我希望支持使用 X11 或 Wayland 的所有 Linux 版本。此外,我最近尝试了 ChromeOS Linux Beta,对它的支持也很好。此外,非常需要对 Windows 和 OSX 的支持。
我已经安装并使用了许多工具 wnck
、xdotool
、wmctrl
、apt-get
,这些工具让我陷入了困境。我希望它们是一个流行的 python 库(最好通过 pip
而不是 pip3
或 using System;
namespace testingParams
{
class Program
{
private void canOnlyBeCalledSlowly(int[] myArr)
{
Console.WriteLine("Snore");
}
private void canBeCalledQuickly(params int[] myArr) {
Console.WriteLine("That was quick");
}
static void Main(string[] args)
{
Program p = new Program();
//We're being conventional here:
int[] myArr = new int[] { 1,2,3,4,5 };
p.canOnlyBeCalledSlowly(myArr);
//We're being quick here:
p.canBeCalledQuickly(1,3);
}
}
}
安装),可以向 python 公开“屏幕”、“桌面”和“监视器”。
解决方法
我回答了我自己的问题。这是阻止您周六晚上午夜入睡的答案之一,因此您在周日凌晨 1:00 起床并编码到凌晨 4:30。
以下是您可以适应非 Ubuntu 环境的代码(使用“未来代码”功能):
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#==============================================================================
#
# m - Wrapper for mserve.py
#
#==============================================================================
'''
Splash screen for mserve.
mserve has it's own list of required modules but this wrapper requires:
Gnome Desktop Toolkit (Gdk)
'''
from __future__ import print_function # Must be first import
try:
import tkinter as tk
PYTHON_VER="3"
except ImportError: # Python 2
import Tkinter as tk
PYTHON_VER="2"
import image as img # Routines for tk & photo images
import mserve # Script loaded as module for .pyc
# https://stackoverflow.com/a/36419702/6929343
import logging
logging.getLogger('PIL').setLevel(logging.WARNING)
import sys
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',level=logging.DEBUG,stream=sys.stdout)
''' Future code '''
def get_active_window():
"""
From: https://stackoverflow.com/a/36419702/6929343
Get the currently active window.
Returns
-------
string :
Name of the currently active window.
"""
import sys
active_window_name = None
logging.info('sys.platform: ' + sys.platform)
print('sys.platform:',sys.platform)
if sys.platform in ['linux','linux2']:
# Alternatives: http://unix.stackexchange.com/q/38867/4784
try:
import wnck
except ImportError:
logging.info("wnck not installed")
wnck = None
if wnck is not None:
screen = wnck.screen_get_default()
screen.force_update()
window = screen.get_active_window()
if window is not None:
pid = window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
else:
try:
# Next 3 limes from: https://stackoverflow.com/a/43349245/6929343
import gi
gi.require_version('Gtk','3.0')
gi.require_version('Wnck','3.0')
# Continue with original code:
from gi.repository import Gtk,Wnck
gi = "Installed"
except ImportError:
logging.info("gi.repository not installed")
gi = None
if gi is not None:
Gtk.init([]) # necessary if not using a Gtk.main() loop
screen = Wnck.Screen.get_default()
screen.force_update() # recommended per Wnck documentation
active_window = screen.get_active_window()
pid = active_window.get_pid()
with open("/proc/{pid}/cmdline".format(pid=pid)) as f:
active_window_name = f.read()
elif sys.platform in ['Windows','win32','cygwin']:
# http://stackoverflow.com/a/608814/562769
import win32gui
window = win32gui.GetForegroundWindow()
active_window_name = win32gui.GetWindowText(window)
elif sys.platform in ['Mac','darwin','os2','os2emx']:
# http://stackoverflow.com/a/373310/562769
from AppKit import NSWorkspace
active_window_name = (NSWorkspace.sharedWorkspace()
.activeApplication()['NSApplicationName'])
else:
print("sys.platform={platform} is unknown. Please report."
.format(platform=sys.platform))
print(sys.version)
print("Active window: %s" % str(active_window_name))
return active_window_name
''' Future code '''
def get_GtkWindow(w):
# From: https://askubuntu.com/a/303754/307523
import gi
gi.require_version('Gdk','3.0')
gi.require_version('Gtk','3.0')
from gi.repository import Gdk,Gtk
# Replace w with the GtkWindow of your application
w = Gtk.Window()
# Get the screen from the GtkWindow
s = w.get_screen()
# Using the screen of the Window,the monitor it's on can be identified
m = s.get_monitor_at_window(s.get_active_window())
# Then get the geometry of that monitor
monitor = s.get_monitor_geometry(m)
# This is an example output
print("Height: %s,Width: %s,X: %s,Y: %s" % \
(monitor.height,monitor.width,monitor.x,monitor.y))
''' Future code '''
def get_monitors():
"""
Get list of monitors in Gnome Desktop
"""
import gi
gi.require_version('Gdk','3.0')
from gi.repository import Gdk
global NUMBER_OF_MONITORS,GNOME,ACTIVE_MONITOR,MONITOR_GEOMETRY
display = Gdk.Display.get_default()
screen = display.get_default_screen()
window = screen.get_active_window()
ACTIVE_MONITOR = screen.get_monitor_at_window(window)
print('ACTIVE_MONITOR:',ACTIVE_MONITOR)
# Gnome version 3.22 developed new monitor object
try:
# Gnome 3.22
NUMBER_OF_MONITORS = display.get_n_monitors()
monitor = display.get_monitor(ACTIVE_MONITOR)
MONITOR_GEOMETRY = monitor.get_geometry()
GNOME=3.22
except:
# Gnome 3.18
NUMBER_OF_MONITORS = screen.get_n_monitors()
MONITOR_GEOMETRY = screen.get_monitor_geometry(ACTIVE_MONITOR)
GNOME=3.18
# collect data about monitors
for index in range(NUMBER_OF_MONITORS):
if GNOME==3.22:
monitor = display.get_monitor(index)
geometry = monitor.get_geometry()
name = monitor.get_monitor_plug_name()
else:
geometry = screen.get_monitor_geometry(index)
name = screen.get_monitor_plug_name(index)
print("Monitor {} = {}x{}+{}+{}".format(index,geometry.width,\
geometry.height,geometry.x,geometry.y),name)
#get_monitors()
#print('ACTIVE_MONITOR:','MONITOR_GEOMETRY:',MONITOR_GEOMETRY)
''' Start of REAL code used today (May 2,2021) '''
def get_window_monitor(window):
"""
Returns the Gdk monitor geometry rectangle tkinter window is on.
If window is off screen force it into Monitor 1 (index 0).
:param window: Tkinter root or Topleel
"""
import gi
gi.require_version('Gdk','3.0')
from gi.repository import Gdk
# global variables that might be useful down the road but not on May 2,2021
global NUMBER_OF_MONITORS,GNOME
display = Gdk.Display.get_default()
screen = display.get_default_screen()
# Gnome version 3.22 deprecated what used to work 3.18.
# Gonme wasn't built in a day but,it was burned over night in next release!
try:
# Gnome 3.22
NUMBER_OF_MONITORS = display.get_n_monitors()
GNOME=3.22
except:
# Gnome 3.18
NUMBER_OF_MONITORS = screen.get_n_monitors()
GNOME=3.18
x = window.winfo_x() # Window's left coordinate on screen
y = window.winfo_y() # Window's top coordinate on screen
if x < 0: x = 0 # Window top left may be off screen!
if y < 0: y = 0
first_monitor = None
for index in range (NUMBER_OF_MONITORS):
if GNOME==3.22:
# Gnome version 3.22 developed new monitor object
monitor = display.get_monitor(index)
mon_geom = monitor.get_geometry()
else:
# Gnome version 3.18 uses screen object for monitor properties
mon_geom = screen.get_monitor_geometry(index)
# Save first monitor if needed later
if not first_monitor:
first_monitor = mon_geom
# Copmare to monitor's coordinates on screen and monitor width x height
if x < mon_geom.x: continue
if x >= mon_geom.x + mon_geom.width: continue
if y < mon_geom.y: continue
if y >= mon_geom.y + mon_geom.height: continue
# Window is comletely on this monitor.
return mon_geom
# If window off of screen use first monitor
return first_monitor
def center(window):
"""
From: https://stackoverflow.com/a/10018670/6929343
centers a tkinter window on monitor in multi-monitor setup
:param win: the main window or Toplevel window to center
"""
window.update_idletasks() # Refresh window's current position
mon_geom=get_window_monitor(window) # Monitor geometry window is on
if mon_geom is None:
logging.error("No monitors found!")
return None
# Calcuate X,Y of window to center within monitors X,Y,width and height
x = mon_geom.width // 2 - window.winfo_width() // 2 + mon_geom.x
y = mon_geom.height // 2 - window.winfo_height() // 2 + mon_geom.y
if x < 0: x = 0 # Window top left may be off screen!
if y < 0: y = 0
window.geometry('+{}+{}'.format(x,y))
window.deiconify() # Forces window to appear
return mon_geom
def main():
"""
Create splash screen and invoke mserve.py which takes a second or more
"""
splash = tk.Tk() # "very top" toplevel
splash.title("Music Server - mserve")
''' Set font style for all fonts including tkSimpleDialog.py '''
img.set_font_style() # Make messagebox text larger for HDPI monitors
''' Get splash image '''
splash_image = img.m_splash_image(300,'white','lightskyblue','black')
# create and pack the canvas. Then load image file
canvas = tk.Canvas(width=300,height=300,bg='black')
canvas.pack(expand=tk.YES,fill=tk.BOTH)
canvas.create_image(0,image=splash_image,anchor=tk.NW)
splash.update_idletasks() # This is required for visibility
# Cemter splash screen on monitor and get monitors geometry
mon_geom=center(splash)
splash.update() # This is required for visibility
# At this point make window undecorated,don't do it sooner!
# From: https://stackoverflow.com/a/37199655/6929343
splash.overrideredirect(True) # Undecorated to prevent close
# Call mserve module about 10k lines of code
mserve.main(toplevel=splash,mon_geom=mon_geom)
exit() # Required to close mserve library
splash.mainloop()
if __name__ == "__main__":
main()
# End of m