I have a window in a window. I wish to place the cursor within the inner-most window. I thought that window.move(y,x) would do the trick, but that seems to have no effect unless called in the outer-most window.
What I want to happen:
┌──────────────────────────────────────┐
│ │
│ 🅃his is win2 │
│ │
│ │
│ │
└──────────────────────────────────────┘
but what actually happens is that the cursor winds up in the upper-left corner of the screen.
My code:
#!/usr/bin/env python3
import curses
import sys
def main():
try:
stdscr = curses.initscr()
win1 = stdscr.derwin(10,40, 4,4)
win2 = win1.derwin(1,20, 2,2)
win1.border()
win2.clear()
win2.addstr(0,0, "This is win2")
win2.move(0,0) # Doesn't work
#win1.move(2,2) # Nor does this
#stdscr.move(6,6) # But this does
stdscr.refresh()
ic = stdscr.getch()
finally:
curses.endwin()
return 0
if __name__ == '__main__':
sys.exit(main())
The only way I can make this work is to find the inner window's absolute position on the screen and pass it to stdscr.move(). But in this module, it's a bit of a hassle to track the upper-most window. There doesn't seem to be a way to take an arbitrary window and find the top-level window.
Related
Am trying to build a Tk project that displays data in a grid format to a Tk window, am fairly new to python Here's the project:
import pandas as pd
import time
from tabulate import tabulate
import csv
from tkinter import Tk, Canvas, Entry, Text, Button, PhotoImage
from pathlib import Path
data = {'name':'kangeso','msg':{'isSuccessful': True, 'statusCode': 200, 'message': [], 'result':{'closed_case':[{'id':[3456456675], 'subject': 'English', 'subject_id':2, 'created':76574767}], 'open_case': [], 'user_balance_id': 1089541798}}, 'request_id': ''}
actual = data['msg']
result = actual['result']
real = result['closed_case']
print(real)
kilo = tabulate(real, headers='keys',tablefmt = 'simple_grid')
print(kilo)
OUTPUT_PATH = Path(__file__).parent
ASSETS_PATH = OUTPUT_PATH / Path("Downloads")
def relative_to_assets(path: str) -> Path:
return ASSETS_PATH / Path(path)
window = Tk()
window.geometry("1440x700")
window.configure(bg = "#0F1014")
canvas = Canvas(
window,
bg = "#0F1014",
height = 700,
width = 1440,
bd = 0,
highlightthickness = 0,
relief = "ridge"
)
canvas.place(x = 0, y = 0)
write_to = canvas.create_rectangle(
0.0,
262.0,
1440.0,
700.0,
fill="#1A1E23",
outline="")
window.resizable(False, False)
window.mainloop()
this is the output without tkdesigner.ie print(kilo) it prints data in a grid format.
┌──────────────┬───────────┬──────────────┬───────────┐
│ id │ subject │ subject_id │ created │
├──────────────┼───────────┼──────────────┼───────────┤
│ [3456456675] │ English │ 2 │ 76574767 │
└──────────────┴───────────┴──────────────┴───────────┘
I would like to get values under subject_id, i used
(real[0])
but the data its just too much i have like hundreds them
I tried to display it using create_text but it gives an error that says int values must be either True or non True.
Hello: I have a few questions:
in the Gallery module widget appears ok to me - 640x480, After import I have a small window, I do not understand why?
How do I move this imported widget to the center of the main window? After import relative to the main window, it appears in the upper right corner. I tried this code fact it can be set but the problem is that here appears a second empty window and this empty one I can operate.
old_code.py
def get_gallery(self):
from GUI.module.gallery import Gallery
Gallery(tk.Toplevel(self.root)).pack(fill=tk.BOTH, expand=1)
x = self.root.winfo_x()
y = self.root.winfo_y()
tk.Toplevel(self.root).geometry("+%d+%d" % (x + 600, y + 800))
How to make this imported window pasted into the main window after import? I.e. so that there are not two windows of the main window and the gallery (after import) but that they constitute one window? Thank you for your help :D
gallery.py
class Gallery(tk.Frame):
code of gallery
if __name__ == '__main__':
root.geometry("%dx%d" % (640, 480))
root.title("Open file")
app = Gallery(tk.Tk())
root.mainloop()
main.py
...
def get_gallery(self):
from GUI.module.gallery import Gallery
Gallery(tk.Toplevel(self.root)).pack()
def get_run_gallery(self):
jpg = tk.Button(self.frame, text="Gallery", command=self.get_gallery)
self.my_canvas.create_window(780, 220, anchor="nw", window=jpg, height=50, width=200)
...
def get_run_first_page():
calling ...
if __name__ == '__main__':
first = MainPage(tk.Tk())
first.get_run_first_page()
There are many questions on how to center a python tkinter window on the screen and the answer works well. My problem is my so-called "screen" looks like this:
Although you can move windows partially (or entirely) to the grey areas they won't actually show up on any of my three monitors. Top left monitor is 1920x1080, top right monitor is 3840x2160 and bottom right monitor is 1920x1080.
A program can be started via desktop icon which could be on any monitor or via gnome-terminal which could be on any monitor. How does one discover:
Which monitor was active when python was invoked?
Coordinates of active monitor within the screen real estate?
Although I'm using Gnome Desktop I'd like support for all Linux flavors using X11 or Wayland. Additionally I tried out ChromeOS Linux Beta lately and support for it would also be nice. Furthermore support for Windows and OSX is highly desired.
I've already installed and used many tools gi, wnck, xdotool, wmctrl that hem me into a corner. I'm hoping their is a popular python library (preferably installed via apt-get and not pip or pip3) that can expose "screen", "desktop" and "monitors" to python.
I answered my own question. It was one of those answers that stops you from falling asleep Saturday night at midnight so you get up at 1:00 am on Sunday and code until 4:30 am.
Here's the code which you can adapt for non-Ubuntu environments (using the "future code" functions):
#!/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
first of all I hope you have me some patience since I'm new on these kind of projects and I also hope not to be asking dumb questions. That being said, my main objective is to create a UI for a raspberry pi 3 which will sense voltage, current, etc from a battery and from a solar panel.
Since I'm working on a raspberry and have some knowledge on Python3, I decided to use QTCreator which as I understand can be translated into python3 through pyqt (https://nikolak.com/pyqt-qt-designer-getting-started/). I installed it on my raspberry pi and made the following UI:
after having a basic UI, I converted the .ui file into .py with the pyuic5 command and I'm able to open the UI with "python3 main.py" and everything seems right:
how the UI looks after opening the main.py file
Now, I want to have a several plots (like voltage against time, etc) on the UI. I'm using the following for testing:
import sys
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
win = pg.GraphicsWindow()
win.setWindowTitle('pyqtgraph example: Scrolling Plots')
p1 = win.addPlot(labels = {'left':'Voltage', 'bottom':'Time'})
data1 = np.random.normal(size=10)
data2 = np.random.normal(size=10)
curve1 = p1.plot(data1, pen=(3,3))
curve2 = p1.plot(data2, pen=(2,3))
ptr1 = 0
def update1():
global data1, curve1, data2, ptr1
data1[:-1] = data1[1:] # shift data in the array one sample left
# (see also: np.roll)
data1[-1] = np.random.normal()
ptr1 += 1
curve1.setData(data1)
curve1.setPos(ptr1,0)
data2[:-1] = data2[1:] # shift data in the array one sample left
# (see also: np.roll)
data2[-1] = np.random.normal()
curve2.setData(data2)
curve2.setPos(ptr1,0)
def update():
update1()
timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(2000) # number of seconds (every 1000) for next update
if __name__ == '__main__':
QtGui.QApplication.instance().exec_()
Is it possible to embbed that plot into my main.py file? If I understood correctly, I'm supposed to use the promoting widget functionality on QTCreator. Thanks in advance guys!
What is promoted through Qt Designer is a Widget, ie a class, so it can not be directly promoted, what we must do is place it inside a class as shown below:
Plotter.py
class CustomWidget(pg.GraphicsWindow):
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
ptr1 = 0
def __init__(self, parent=None, **kargs):
pg.GraphicsWindow.__init__(self, **kargs)
self.setParent(parent)
self.setWindowTitle('pyqtgraph example: Scrolling Plots')
p1 = self.addPlot(labels = {'left':'Voltage', 'bottom':'Time'})
self.data1 = np.random.normal(size=10)
self.data2 = np.random.normal(size=10)
self.curve1 = p1.plot(self.data1, pen=(3,3))
self.curve2 = p1.plot(self.data2, pen=(2,3))
timer = pg.QtCore.QTimer(self)
timer.timeout.connect(self.update)
timer.start(2000) # number of seconds (every 1000) for next update
def update(self):
self.data1[:-1] = self.data1[1:] # shift data in the array one sample left
# (see also: np.roll)
self.data1[-1] = np.random.normal()
self.ptr1 += 1
self.curve1.setData(self.data1)
self.curve1.setPos(self.ptr1, 0)
self.data2[:-1] = self.data2[1:] # shift data in the array one sample left
# (see also: np.roll)
self.data2[-1] = np.random.normal()
self.curve2.setData(self.data2)
self.curve2.setPos(self.ptr1,0)
if __name__ == '__main__':
w = CustomWidget()
w.show()
QtGui.QApplication.instance().exec_()
Before proceeding I will assume that the files have the following structure:
.
├── main.py
└── Plotter.py
The first thing to do is choose the widget:
Then we right click on this and choose the option to promote to..:
In the dialog box we place CustomWidget in Promoted Class Name and Plotter.h in Header File, then press the Add and Promote button.
Then we convert our .ui file to .py
I have a main python program A.py that calls a GUI Gtk python program, B.py, to display a window.
I want this window being color buttons and when I click on one, the main A.py code recover a value, the RGB color value.
A.py
import B
c = B.gui_color()
print(c)
B.py
class W(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="colors")
self.box = Gtk.Box(spacing=0, homogeneous=True)
self.add(self.box)
colors = j.load("colors.json")
for c in colors:
b = Gtk.Button()
b.connect("clicked", self.return_color, c["value"])
# x257 to get the GTK color
b.modify_bg(Gtk.StateType.NORMAL, Gdk.Color(c["value"][0] * 257, c["value"][1] * 257, c["value"][2] * 257))
self.box.pack_start(b, True, True, 0)
def return_color(self, widget, *color):
self.close()
return color[0]
def gui_color():
w = W()
w.connect("destroy", Gtk.main_quit)
w.show_all()
Gtk.main()
Everything is ok with the program, I get my window with multiple color buttons, however I can not figure out how to recover the color I clicked on. The return action in the return_color doesn't return to the A.py program. How can I perform that? Should I use stdout with print? I precise that after having clicking, I would like to do others actions that won't need a GUI at all.
Before return color[0] in the return_color do self.selected_color = color[0], after Gtk.main() do return w.selected_color