【问题标题】:Python centering window on "monitor" not "screen" or "desktop"“监视器”而不是“屏幕”或“桌面”上的 Python 居中窗口
【发布时间】:2021-07-24 20:30:29
【问题描述】:

关于如何在屏幕上居中显示 python tkinter 窗口有很多问题,答案很好。我的问题是我所谓的“屏幕”是这样的:

尽管您可以将窗口部分(或全部)移动到灰色区域,但它们实际上不会显示在我的三台显示器上。左上显示器为 1920x1080,右上显示器为 3840x2160,右下显示器为 1920x1080。

可以通过可以在任何显示器上的桌面图标或通过可以在任何显示器上的gnome-terminal 启动程序。如何发现:

  • 调用 python 时哪个监视器处于活动状态?
  • 屏幕空间内活动监视器的坐标?

虽然我使用的是 Gnome Desktop,但我希望支持使用 X11 或 Wayland 的所有 Linux 风格。此外,我最近试用了 ChromeOS Linux Beta,对它的支持也很好。此外,还非常需要对 Windows 和 OSX 的支持。

我已经安装并使用了许多工具giwnckxdotoolwmctrl,它们让我陷入了困境。我希望他们是一个流行的python库(最好通过apt-get而不是pippip3安装),可以将“屏幕”、“桌面”和“监视器”暴露给python。

【问题讨论】:

    标签: python screen desktop monitor


    【解决方案1】:

    我回答了我自己的问题。这是阻止您在周六晚上午夜入睡的答案之一,因此您在周日凌晨 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:', 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, 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
    

    【讨论】:

      猜你喜欢
      • 2022-10-06
      • 2010-12-23
      • 2011-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-26
      • 2021-10-01
      相关资源
      最近更新 更多