python tkinter with a simple web wrapper - python

I am writing a simple python program with a UI, and i can't find out how to put a web page inside the program. I tried searching on Google, but i can't find any simple implementations.
Any links would be great. Just to recap, i just want a simple GUI python program with a webpage embeded inside it, basically like this:
# File: hello1.py
from Tkinter import *
root = Tk()
w = Label(root, text="This is where i was the web page embeded")
w.pack()
root.mainloop()
Thanks,
EDIT:
Solved the problem, used webkit
#!/usr/bin/env python
import gtk
import webkit
import gobject
gobject.threads_init()
win = gtk.Window()
bro = webkit.WebView()
bro.open("http://www.google.com")
win.add(bro)
win.show_all()
gtk.main()
Jack

You can both embed HTML and open full web-pages (with CSS and javascript even) in tkinter. With the cefpython module you can embed a full-blown Chromium browser in a tk window.
from cefpython3 import cefpython as cef
import ctypes
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
import sys
import platform
import logging as _logging
# Fix for PyCharm hints warnings
WindowUtils = cef.WindowUtils()
# Platforms
WINDOWS = (platform.system() == "Windows")
LINUX = (platform.system() == "Linux")
MAC = (platform.system() == "Darwin")
# Globals
logger = _logging.getLogger("tkinter_.py")
class MainFrame(tk.Frame):
def __init__(self, root):
self.browser_frame = None
self.navigation_bar = None
# Root
root.geometry("900x640")
tk.Grid.rowconfigure(root, 0, weight=1)
tk.Grid.columnconfigure(root, 0, weight=1)
# MainFrame
tk.Frame.__init__(self, root)
self.master.title("Tkinter example")
self.master.protocol("WM_DELETE_WINDOW", self.on_close)
self.master.bind("<Configure>", self.on_root_configure)
self.bind("<Configure>", self.on_configure)
self.bind("<FocusIn>", self.on_focus_in)
self.bind("<FocusOut>", self.on_focus_out)
# NavigationBar
self.navigation_bar = NavigationBar(self)
self.navigation_bar.grid(row=0, column=0,
sticky=(tk.N + tk.S + tk.E + tk.W))
tk.Grid.rowconfigure(self, 0, weight=0)
tk.Grid.columnconfigure(self, 0, weight=0)
# BrowserFrame
self.browser_frame = BrowserFrame(self, self.navigation_bar)
self.browser_frame.grid(row=1, column=0,
sticky=(tk.N + tk.S + tk.E + tk.W))
tk.Grid.rowconfigure(self, 1, weight=1)
tk.Grid.columnconfigure(self, 0, weight=1)
# Pack MainFrame
self.pack(fill=tk.BOTH, expand=tk.YES)
def on_root_configure(self, _):
logger.debug("MainFrame.on_root_configure")
if self.browser_frame:
self.browser_frame.on_root_configure()
def on_configure(self, event):
logger.debug("MainFrame.on_configure")
if self.browser_frame:
width = event.width
height = event.height
if self.navigation_bar:
height = height - self.navigation_bar.winfo_height()
self.browser_frame.on_mainframe_configure(width, height)
def on_focus_in(self, _):
logger.debug("MainFrame.on_focus_in")
def on_focus_out(self, _):
logger.debug("MainFrame.on_focus_out")
def on_close(self):
if self.browser_frame:
self.browser_frame.on_root_close()
self.master.destroy()
def get_browser(self):
if self.browser_frame:
return self.browser_frame.browser
return None
def get_browser_frame(self):
if self.browser_frame:
return self.browser_frame
return None
class BrowserFrame(tk.Frame):
def __init__(self, master, navigation_bar=None):
self.navigation_bar = navigation_bar
self.closing = False
self.browser = None
tk.Frame.__init__(self, master)
self.bind("<FocusIn>", self.on_focus_in)
self.bind("<FocusOut>", self.on_focus_out)
self.bind("<Configure>", self.on_configure)
self.focus_set()
def embed_browser(self):
window_info = cef.WindowInfo()
rect = [0, 0, self.winfo_width(), self.winfo_height()]
window_info.SetAsChild(self.get_window_handle(), rect)
self.browser = cef.CreateBrowserSync(window_info,
url="http://www.google.com") #todo
assert self.browser
self.browser.SetClientHandler(LoadHandler(self))
self.browser.SetClientHandler(FocusHandler(self))
self.message_loop_work()
def get_window_handle(self):
if self.winfo_id() > 0:
return self.winfo_id()
elif MAC:
from AppKit import NSApp
import objc
return objc.pyobjc_id(NSApp.windows()[-1].contentView())
else:
raise Exception("Couldn't obtain window handle")
def message_loop_work(self):
cef.MessageLoopWork()
self.after(10, self.message_loop_work)
def on_configure(self, _):
if not self.browser:
self.embed_browser()
def on_root_configure(self):
# Root <Configure> event will be called when top window is moved
if self.browser:
self.browser.NotifyMoveOrResizeStarted()
def on_mainframe_configure(self, width, height):
if self.browser:
if WINDOWS:
ctypes.windll.user32.SetWindowPos(
self.browser.GetWindowHandle(), 0,
0, 0, width, height, 0x0002)
elif LINUX:
self.browser.SetBounds(0, 0, width, height)
self.browser.NotifyMoveOrResizeStarted()
def on_focus_in(self, _):
logger.debug("BrowserFrame.on_focus_in")
if self.browser:
self.browser.SetFocus(True)
def on_focus_out(self, _):
logger.debug("BrowserFrame.on_focus_out")
if self.browser:
self.browser.SetFocus(False)
def on_root_close(self):
if self.browser:
self.browser.CloseBrowser(True)
self.clear_browser_references()
self.destroy()
def clear_browser_references(self):
self.browser = None
class LoadHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnLoadStart(self, browser, **_):
if self.browser_frame.master.navigation_bar:
self.browser_frame.master.navigation_bar.set_url(browser.GetUrl())
class FocusHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnTakeFocus(self, next_component, **_):
logger.debug("FocusHandler.OnTakeFocus, next={next}"
.format(next=next_component))
def OnSetFocus(self, source, **_):
logger.debug("FocusHandler.OnSetFocus, source={source}"
.format(source=source))
return False
def OnGotFocus(self, **_):
"""Fix CEF focus issues (#255). Call browser frame's focus_set
to get rid of type cursor in url entry widget."""
logger.debug("FocusHandler.OnGotFocus")
self.browser_frame.focus_set()
class NavigationBar(tk.Frame):
def __init__(self, master):
self.back_state = tk.NONE
self.forward_state = tk.NONE
self.back_image = None
self.forward_image = None
self.reload_image = None
tk.Frame.__init__(self, master)
# Back button
back = b'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgBAMAAAAQtmoLAAAAKlBMVEUAAAAyMjIzMzMzMzMyMjIzMzMxMTEzMzMtLS0zMzMkJCQyMjIrKysxMTExeYOJAAAADnRSTlMAgH3/cGlU1BHPDsoMWDQYbKYAAACNSURBVHgB7deBBYBAAIXha4YWaIdXJYlGaJwAzRFojhMarQnO+eWg3g9AvijnXXDZnKuawKpbCEgLA6QOAtLKAKmHgDYIDLuBDDBS4KDA+TsgQmC6IXAZyADpEkA6CogC/AH+SpQIlIAf7v2vYSIGfJBR4ihFmNjx+KHE8AliLjlD6dAFUxqMdX4dcM49/KeCIizV7gUAAAAASUVORK5CYII='
self.back_image = tk.PhotoImage(data=back)
self.back_image = self.back_image.zoom(25).subsample(100)
self.back_button = tk.Button(self, image=self.back_image,command=self.go_back)
self.back_button.grid(row=0, column=0)
# Forward button
forward = b'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAA1UlEQVR4Ae3cAQbCYBzG4Q6QiI4wYIbtPU1HCAgQSOcLgtBtamHQAb5/43noAu8Pmu37NgAAAAAAAAAAAC0luU7TdNuUMP4lyfv7E6GxefDzMr4IheOL0FiS0zK2CI2N43hcRhahQJJDkqcIhYZh2IsgAiKIgAgi8BPhURiBrut2IoiACCIgggj8WQQR5mHvhRHo+35bEAERBMD4xl8V4/v7iQcw42N842N8L+XxWYrxMb7xMb5TMsZ/GX+Nh/RwTNVBbVxV4LIOXFcDAAAAAAAAAADwAephJFWvaMW3AAAAAElFTkSuQmCC'
self.forward_image = tk.PhotoImage(data=forward)
self.forward_image = self.forward_image.zoom(25).subsample(100)
self.forward_button = tk.Button(self, image=self.forward_image,command=self.go_forward)
self.forward_button.grid(row=0, column=1)
# Reload button
refresh = b'iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAQAAABIkb+zAAADwklEQVR4Ae2aY5jGVhCFZ+3a9iK7+XLPqW23v2rbtm3btm3btm3bbWpN8i0nt83z3Hd+Z+ac6GokEAgEAoFAIBAIBKyIGjkr1sJBuIoP8zV8w6/4Oh/GVTgIayWzRI0yJLiT+COZGjvzZnzLtJ/4Grdg+3gcGRTchqn4oG90bsvHmQ46vsPZycyDke/BgJsUR/IrpkMP3M0lpVaqgE2YFm+ghusNUbyOp9081eQXbqAyIW5hahBXurGz8gs3wOX4KVOjeBuUv+C6TIs2UMfjmVoGvnXLKPnFGWArru9Xzgs8kxu5mX7/53d2xJ2cCytwW57Jl/qx8BO3VfKLMTBVE++tfh9xUtwp/cA+nMbvq15/A9PCDfC8KsW/4IG948og6J0Yh/Pr/CyFG3Bb55b5EvtGY8gQiCfitfRvAAvgp5wiz7hJh/cfw8deDVSmwefZArg8bpNhEo2HW/wZqMGjOQWOkloZAWzgeZ4MuNVz0p9gcFtO8mIgauf72SmA1MoIUfKLM4Dts59u1C4jhEd5+oijdn6iPt1v2GUl34MBbKrTYhdD+R4MPKLSvj5Vk5V8DwZcTybt5jIicDBTjwawh3p9Pu/sMJTvwcD9evCyk+/BQDKanv9UnJ18DwawmEr5gQwb7s3Uv4Gt1BdwgaV8DwZ4gkq5haV8DwZw678TuoWkXOAp9QpNJeWCr6uHOpaUC719NVGLlAu9bo0aS/4KdY0p5YJPl/wjxm3/NpAsLOUCJ6onsKWUC26jfqPnSblwi2cmc+UiatfTaVeRcoH71FdwpBQETlNP+3YRD0tKK9wE/L6QH0Yl0lNct5kUAA7SdfqmEBvwqPW2Su5O9Reqyh1iBTbR9wY7izE8S9dwq4sVE7XoKR2+iTvFkGTm7NGr6bQR25lu7iriNj6Tyb+u9Wjwni6Bq6RWLKjLObZ90Xza7lYzO+BQ8JRsZjePmFPDh+2PmKQWh+RkvViKIO7MO+TjFXHbCHb9bszJ+KlqiLLDLWR5zOqmxStMs+GWEU3RB934AvtyrCH+mPfDj7m5DpJiwTnVWw04/uDEc2V179WfrVCmasL9w232YEMyH07ml0yryWeDGGDSboMZf/+TT9bM8dGdzMKNeIWSruM8qRdP1OmVskEcJTXiEyzFT8zEf+1WE//0TmzU9PdgMrX8R9Rg/RG2Xb7lVjN6dfw3vvJTt4PBVrFZ6/FjQxL/KrY32GX11PytpR+ZzCe1/+P2+2SWP9rvH/qt/f73lsoXcSF3xGLxRGJEIBAIBAKBQCAQCPwM9/tgs3UybYkAAAAASUVORK5CYII='
self.reload_image = tk.PhotoImage(data=refresh)
self.reload_image = self.reload_image.zoom(25).subsample(100)
self.reload_button = tk.Button(self, image=self.reload_image,command=self.reload)
self.reload_button.grid(row=0, column=2)
# Url entry
self.url_entry = tk.Entry(self)
tk.Grid.rowconfigure(self, 0, weight=100)
tk.Grid.columnconfigure(self, 3, weight=100)
# Update state of buttons
self.update_state()
def go_back(self):
if self.master.get_browser():
self.master.get_browser().GoBack()
def go_forward(self):
if self.master.get_browser():
self.master.get_browser().GoForward()
def reload(self):
if self.master.get_browser():
self.master.get_browser().Reload()
def set_url(self, url):
self.url_entry.delete(0, tk.END)
self.url_entry.insert(0, url)
def on_url_focus_in(self, _):
logger.debug("NavigationBar.on_url_focus_in")
def on_url_focus_out(self, _):
logger.debug("NavigationBar.on_url_focus_out")
def on_load_url(self, _):
if self.master.get_browser():
self.master.get_browser().StopLoad()
self.master.get_browser().LoadUrl(self.url_entry.get())
def on_button1(self, _):
"""Fix CEF focus issues (#255). See also FocusHandler.OnGotFocus."""
logger.debug("NavigationBar.on_button1")
self.master.master.focus_force()
def update_state(self):
browser = self.master.get_browser()
if not browser:
if self.back_state != tk.DISABLED:
self.back_button.config(state=tk.DISABLED)
self.back_state = tk.DISABLED
if self.forward_state != tk.DISABLED:
self.forward_button.config(state=tk.DISABLED)
self.forward_state = tk.DISABLED
self.after(100, self.update_state)
return
if browser.CanGoBack():
if self.back_state != tk.NORMAL:
self.back_button.config(state=tk.NORMAL)
self.back_state = tk.NORMAL
else:
if self.back_state != tk.DISABLED:
self.back_button.config(state=tk.DISABLED)
self.back_state = tk.DISABLED
if browser.CanGoForward():
if self.forward_state != tk.NORMAL:
self.forward_button.config(state=tk.NORMAL)
self.forward_state = tk.NORMAL
else:
if self.forward_state != tk.DISABLED:
self.forward_button.config(state=tk.DISABLED)
self.forward_state = tk.DISABLED
self.after(100, self.update_state)
if __name__ == '__main__':
logger.setLevel(_logging.INFO)
stream_handler = _logging.StreamHandler()
formatter = _logging.Formatter("[%(filename)s] %(message)s")
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
logger.info("CEF Python {ver}".format(ver=cef.__version__))
logger.info("Python {ver} {arch}".format(
ver=platform.python_version(), arch=platform.architecture()[0]))
logger.info("Tk {ver}".format(ver=tk.Tcl().eval('info patchlevel')))
assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this"
sys.excepthook = cef.ExceptHook # To shutdown all CEF processes on error
root = tk.Tk()
app = MainFrame(root)
# Tk must be initialized before CEF otherwise fatal error (Issue #306)
cef.Initialize()
app.mainloop()
cef.Shutdown()

Try using Tkinterweb!
https://pypi.org/project/tkinterweb/
Demo code:
from tkinterweb import HtmlFrame #import the HTML browser
try:
import tkinter as tk #python3
except ImportError:
import Tkinter as tk #python2
root = tk.Tk() #create the tkinter window
frame = HtmlFrame(root) #create HTML browser
frame.load_website("http://google.com") #load a website
frame.pack(fill="both", expand=True) #attach the HtmlFrame widget to the parent window
root.mainloop()

How about using pywebview ?
It doesn't run in tkinter. but it is simple.
https://pypi.org/project/pywebview/
import webview
webview.create_window('Hello world', 'https://pywebview.flowrl.com/hello')
webview.start()

the simple answer will be is stop wasting your time please learn electron js for making apps b/c after this when you try to compile your tkinter project will be so ugly so learn js then electron my suggestion

Related

How to import a scrollable frame from one script into a frame in a different script tkinter

Sorry for the confusing title basically what I'm trying to figure out how to import a frame from one script into another. I'm not sure how I would call it since it has so many functions. Here are my two scripts:
File Name - wrapper:
import tkinter as tk
import workingcatch
from tkinter import *
root = tk.Tk()
outputframe = LabelFrame(master=root, width=800, height=700) #where i want to import the script
outputframe.pack(side=LEFT, padx=10,pady=10)
root.geometry('1280x720')
root.mainloop()
File Name - workingcatch:
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
import tkinter as tk
import logging
import time
import sys
info = logging.getLogger(__name__).info
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
platform = windows = 'mswin'
# define dummy subprocess to generate some output
cmd = [sys.executable or "python", "-u", "-c", """
import itertools, time
for i in itertools.count():
print(i)
time.sleep(0.5)
exit()
""", "exit"]
class OStream(Thread):
enable_print = True
def handel_line(self, line):
if platform == windows:
# Windows uses: "\r\n" instead of "\n" for new lines.
line = line.replace(b"\r\n", b"\n")
if self.enable_print:
info("got: %r", line)
if self.stream_print is not None:
self.stream_print(line)
def stop(self):
self.alive = False
def run(self):
while self.alive:
for s in self.ostrams:
line = s.stdout.readline()
if line:
self.handel_line(line)
time.sleep(0.2)
info("OStream Exit")
def pipe_proc(self, stream):
self.ostrams.append(stream)
def stream_callback(self, func):
self.stream_print = func
def __init__(self):
self.ostrams = []
self.alive = True
self.stream_print = None
Thread.__init__(self)
class Scrolable_Frame(tk.Frame):
def get(self):
return self.interior
def __init__(self, master):
tk.Frame.__init__(self, master)
self.scroll = tk.Scrollbar(self)
self.scroll.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas = tk.Canvas(
self, bd=0, highlightthickness=0,
yscrollcommand=self.scroll.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.TRUE)
self.scroll.config(command=self.canvas.yview)
self.canvas.xview_moveto(0)
self.canvas.yview_moveto(0)
self.interior = tk.Frame(self.canvas)
interior_id = self.canvas.create_window(
0, 0, window=self.interior, anchor=tk.NW
)
def _configure_interior(_):
size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight())
self.canvas.config(scrollregion="0 0 %s %s" % size)
if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
self.canvas.config(width=self.interior.winfo_reqwidth())
self.interior.bind('<Configure>', _configure_interior)
def _configure_canvas(_):
if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
self.canvas.itemconfigure(interior_id, width=self.canvas.winfo_width())
self.canvas.bind('<Configure>', _configure_canvas)
def _on_mousewheel(event):
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), 'units')
self.canvas.bind_all("<MouseWheel>", _on_mousewheel)
class ShowProcessOutputDemo(tk.Tk):
def __init__(self):
"""Start subprocess, make GUI widgets."""
tk.Tk.__init__(self)
self.geometry('300x200+500+300')
self.protocol("WM_DELETE_WINDOW", self.stop)
self.proc = Popen(cmd, stdout=PIPE, stderr=STDOUT)
self.ostream = OStream()
self.ostream.pipe_proc(self.proc)
self.ostream.stream_callback(self.add_label)
self.ostream.start()
self.exit_button = tk.Button(
self, text="Stop subprocess", command=self.stop
)
self.exit_button.pack(pady=20)
self.scolable_frame = Scrolable_Frame(self)
self.scolable_frame.pack(
expand=True, fill=tk.BOTH, pady=20, padx=20
)
def add_label(self, line):
line_text = 'OStream line content: {0}'.format(line[:-1].decode())
tk.Label(
self.scolable_frame.get(), text=line_text
).pack(anchor=tk.CENTER, expand=True, fill=tk.X)
def stop(self):
"""Stop subprocess and quit GUI."""
self.ostream.stop()
self.proc.kill()
self.proc.stdout.close()
self.proc.wait(timeout=2)
info("GUI Exit")
self.quit()
if __name__ == '__main__':
app = ShowProcessOutputDemo()
app.mainloop()
So what I'm trying to do is get the output from "workingcatch" into 'wrapper's frame.
Thanks for reading

cefpython3 block UI on windows with tkinter

I'm making an application with tkinter in python 3.7.8.
I have to show a map so I use Folium to generate it and cefpython3 to show it (as it generates as a html with javascript)
It works wonderful on Linux, but when I try to test it on Windows 10, the map is shown, i can click on buttons, but cant interact with any tkinter.Entry unless i click on another window, and then come back to the window program.
I assumed it was a focus problem, but even trying to set the focus of the cefpython3 browser to False, the problem keep happening.
This is the MapVisualizer Code
class MapVisualizer(tk.Frame):
""" Generates the map with folium and then attach cefpython3 browser to the frame """
def __init__(self, root, size, mapPoints, zoom, pointDatas=None, **kargs):
self.browser_frame = None
self.navigation_bar = None
self.replaceFolium()
m = folium.Map(location=mapPoints, zoom_start=zoom, height=size[0], width=size[1], min_zoom=3, max_zoom=12)
for data_point in pointDatas:
folium.Marker(data_point[0], popup=data_point[1]).add_to(m)
m.save(resource_path("data/map_location.html"))
# Root
tk.Grid.rowconfigure(root, 0, weight=1)
tk.Grid.columnconfigure(root, 0, weight=1)
# MainFrame
tk.Frame.__init__(self, root)
# BrowserFrame
tk.Grid.rowconfigure(self, 1, weight=1)
tk.Grid.columnconfigure(self, 0, weight=1)
# Pack MainFrame
self.browser_frame = BrowserFrame(self, self.navigation_bar)
self.browser_frame.grid(row=1, column=0,
sticky=(tk.N + tk.S + tk.E + tk.W))
def on_root_configure(self, _):
if self.browser_frame:
self.browser_frame.on_root_configure()
def on_configure(self, event):
if self.browser_frame:
width = event.width
height = event.height
if self.navigation_bar:
height = height - self.navigation_bar.winfo_height()
self.browser_frame.on_mainframe_configure(width, height)
def on_focus_in(self, _):
self.browser.SetFocus(False)
def on_focus_out(self, _):
self.browser.SetFocus(False)
def on_close(self):
if self.browser_frame:
self.browser_frame.on_root_close()
self.master.destroy()
def get_browser(self):
if self.browser_frame:
return self.browser_frame.browser
return None
def get_browser_frame(self):
if self.browser_frame:
return self.browser_frame
return None
def onClose(self):
cef.Shutdown()
This is the Browser code
class BrowserFrame(tk.Frame):
def __init__(self, master, navigation_bar=None):
self.navigation_bar = navigation_bar
cef.Initialize()
self.closing = False
self.browser = None
tk.Frame.__init__(self, master)
self.bind("<FocusIn>", self.on_focus_in)
self.bind("<FocusOut>", self.on_focus_out)
self.bind("<Configure>", self.on_configure)
self.focus_set()
def embed_browser(self):
window_info = cef.WindowInfo()
rect = [0, 0, self.winfo_width(), self.winfo_height()]
window_info.SetAsChild(self.get_window_handle(), rect)
self.browser = cef.CreateBrowserSync(window_info,
url=f"file:///{os.getcwd()}/data/map_location.html") #todo
assert self.browser
self.browser.SetClientHandler(LoadHandler(self))
self.browser.SetClientHandler(FocusHandler(self))
self.message_loop_work()
def get_window_handle(self):
if self.winfo_id() > 0:
return self.winfo_id()
elif MAC:
from AppKit import NSApp
import objc
return objc.pyobjc_id(NSApp.windows()[-1].contentView())
else:
raise Exception("Couldn't obtain window handle")
def message_loop_work(self):
cef.MessageLoopWork()
self.after(10, self.message_loop_work)
def on_configure(self, _):
if not self.browser:
self.embed_browser()
def on_root_configure(self):
# Root <Configure> event will be called when top window is moved
if self.browser:
self.browser.NotifyMoveOrResizeStarted()
def on_mainframe_configure(self, width, height):
if self.browser:
if WINDOWS:
ctypes.windll.user32.SetWindowPos(
self.browser.GetWindowHandle(), 0,
0, 0, width, height, 0x0002)
elif LINUX:
self.browser.SetBounds(0, 0, width, height)
self.browser.NotifyMoveOrResizeStarted()
def on_focus_in(self, _):
if self.browser:
self.browser.SetFocus(True)
def on_focus_out(self, _):
if self.browser:
self.browser.SetFocus(False)
def on_root_close(self):
if self.browser:
self.browser.CloseBrowser(True)
self.clear_browser_references()
self.destroy()
def clear_browser_references(self):
self.browser = None
class LoadHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnLoadStart(self, browser, **_):
if self.browser_frame.master.navigation_bar:
self.browser_frame.master.navigation_bar.set_url(browser.GetUrl())
class FocusHandler(object):
def __init__(self, browser_frame):
self.browser_frame = browser_frame
def OnTakeFocus(self, next_component, **_):
self.browser_frame.browser.SetFocus(False)
def OnSetFocus(self, source, **_):
self.browser_frame.browser.SetFocus(False)
return False
def OnGotFocus(self, **_):
self.browser_frame.browser.SetFocus(False)
There is no error or debug message, and as I said, this problem only happens on Windows (same env directory and everything).
I had to write a small crutch that opens a transparent window for a millisecond, programmatically the cefpython browser focus is reset and it becomes possible to install it on the tk.entry widget.
entrynum = ttk.Entry(self.f_top)
entrynum.pack(side=tk.LEFT)
entrynum.bind('<Button-1>', lambda e, f=entrynum: b1(e, f))
entrynum.focus_set()
def b1(e, f):
toprs = tk.Toplevel()
toprs.geometry(f'+{0}+{0}')
toprs.attributes('-alpha', 0.0)
tf = ttk.Frame(toprs)
tf.pack()
toprs.state('iconic')
toprs.state('zoomed')
toprs.attributes("-topmost", True)
toprs.after(1, lambda: f.focus_set())
toprs.after(2, lambda: toprs.destroy())
toprs.mainloop()

tkinter- subclass of button not placing on screen from inside another class

I'm making a ide for brainf*ck in python using tkinter and I'm adding a recent projects section but when I'm placing the buttons they do not appear on the screen.
Here is the code for the Scene:
from tkinter import *
from tkinter import filedialog as File
import tkinter as tk
class HoverButton(tk.Button):
def __init__(self, master, **kw):
tk.Button.__init__(self, master=master, **kw)
self.defaultBackground = "#5d5d5d"
self['background'] = self.defaultBackground
self['activebackground'] = "#6d6d6d"
self.bind("<Enter>", self.on_enter)
self.bind("<Leave>", self.on_leave)
def on_enter(self, e):
self['background'] = "#6d6d6d"
def on_leave(self, e):
self['background'] = self.defaultBackground
class ProjectPage(Frame):
def __init__(self, master, projects=[]):
super().__init__(master)
self.projects = projects
self.buttons = []
self.mstr = self.master.master
self.title = "PesolIde: Projets"
self.width = 800
self.height = 500
self.color = "#4d4d4d"
self.projectFrame = tk.Frame(self.mstr,width=800,height=50,bg="#5d5d5d")
self.newProject = HoverButton(self.mstr,text="New Project", height=1, bg="#6d6d6d")
self.openProject = HoverButton(self.mstr,text="Open Project", height=1,bg="#6d6d6d", command=OpenAsk)
self.projectdisplay = tk.Frame(self.mstr, width=700, height=300, bg="#5d5d5d", highlightbackground="black", highlightthickness=1)
for i in range(len(self.projects)):
self.buttons.append(HoverButton(master, text=self.projects[i].split(':')[0], width=50, height=1))
if len(self.buttons)>=40:
break
self.loaded = False
def show(self):
self.projectFrame.place(x=0, y=0)
self.newProject.place(x=20, y=10)
self.openProject.place(x=120, y=10)
self.projectdisplay.place(x=50,y=100)
self.y = 100
print(len(self.buttons))
for i in range(len(self.buttons)):
print("placing " + str(self.buttons[i]))
self.buttons[i].place(x=50,y=100+(20*i))
self.master.set(title=self.title,width=self.width,height=self.height)
self.master.master['bg'] = self.color
def hide(self):
self.newProject.place_forget()
self.openProject.place_forget()
def load(self):
if not self.loaded:
self.newProject.place_forget()
self.openProject.place_forget()
self.loaded = True
def unload(self):
self.newProject.destroy()
self.openProject.destroy()
def OpenAsk():
name = File.askopenfilename()
and here is the code for main.py:
from tkinter import *
import src.framework.modules.Window.Window as windows
import src.framework.src.Scenes.all as Scenes
import tkinter as tk
root = tk.Tk()
window = windows.window(root, "", 800, 500)
window.place()
projects = open("projects.txt",'r').read().split("\n")
start = Scenes.ProjectPage.ProjectPage(window,projects)
start.show()
window.mainloop()
When I make a HoverButton outside the ProjectPage class in the ProjectPage file, it appears as expected but not when initialised from within the class of directly from the main file.
Here are some screenshots.
The output from running main.py:
The output from running from outside the ProjectPage class with the code on the left:
Try to insert values for relx, rely, relwidth, relheight as attributes in "place" or you can as well insert height, width as attributes for place.
Check out the documentation: https://www.tutorialspoint.com/python/tk_place.htm

communication between tkinter toplevels

I'm working on a little project and made a little on-screen keyboard as a tkinter Toplevel
my application is buildt like this:
Root-Window (Tk-Widget)
input 1 (Entry-Widget)
input 2 (Entry-Widget)
input 3 (Text-Widget)
on_screen-keyboard (Toplevel-Widget)
the Toplevel-Widget contains Buttons, with callbacks that should enter text in the entries (just like keyboard-Buttons)
What I want is a communication between children of the keyboard/the keyboard and the last active input-Widget. My Problem is, that I don't know, how to say the keyboard, to which input-Widget it should send the message.
import tkinter as tk
from tkinter import ttk
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.active_input = tk.Variable(value=None)
ttk.Button(self, text="show Keyboard", command=lambda: Keyboard(self)).pack()
self.text = tk.StringVar(value="")
self.input1 = ttk.Entry(self)
self.input1.bind("<FocusIn>", lambda e: self.active_input.set(self.input1))
self.input2 = ttk.Entry(self)
self.input2.bind("<FocusIn>", lambda e: self.active_input.set(self.input2))
self.input3 = tk.Text(self, height=3, width=15)
self.input3.bind("<FocusIn>", lambda e: self.active_input.set(self.input3))
self.input1.pack()
self.input3.pack()
self.input2.pack()
class Keyboard(tk.Toplevel):
OPENED = False
NAME = "- Keyboard -"
NUM = [{"text":"1", "width":1},
{"text":"2", "width":1},
{"text":"3", "width":2}]
CHAR= [{"text":"A", "width":1},
{"text":"B", "width":1},
{"text":"C", "width":2}]
def __init__(self, master):
if not Keyboard.OPENED:
Keyboard.OPENED = True
print("keyboard opened!")
self.master = master
tk.Toplevel.__init__(self, master)
self.title(self.NAME)
self.protocol("WM_DELETE_WINDOW", self.close)
self.keyb_nb = ttk.Notebook(self)
self.keyb_nb.pack()
self.num_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.num_tab, Keyboard.NUM,2)
self.keyb_nb.add(self.num_tab, text="123")
self.char_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.char_tab, Keyboard.CHAR, 2)
self.keyb_nb.add(self.char_tab, text="ABC")
def createPad(self, master, pad:list, max_col):
self.co_count = 0
self.ro = 1
for button in pad:
button["id"] = ttk.Button(master, width=6*button["width"], text=button["text"], command=self.bclicked(button))
if self.co_count >= max_col:
self.ro = self.ro + 1
self.co_count = 0
button["id"].grid(row=self.ro, columnspan=button["width"], column=self.co_count)
self.co_count = self.co_count+button["width"]
def bclicked(self, button:dict):
"""
reciver = self.master.active_input #I think the Problem here is, that the variable contains a string, not a widget
reciever.focus_force()
reciever.insert(index=tk.INSERT, string=button["text"])
"""
pass
def close(self):
Keyboard.OPENED = False
self.destroy()
print("keyboard closed!")
root = MainWindow()
root.mainloop()
Here the init of the Mainwindow and the bclicked of the Keyboard class are important...
the code is debug-ready
I would prefer a solution, similar to the communication in the internet (sender=button, receiver-id, message), but very welcome every working solution
btw: I'm also looking for a solution, how I don't have to force the input to focus and the Toplevel stays an the highest layer of the screen (that if I focus the Tk-Widget/one of the inputs/the button, the keyboard will stay in front of it)
SUMMARY: how do I find out, which of the 3 input-widgets was active at last, when the keyboard-toplevel has already the focus?
I may made more changes than needed, but mainly focus on the method keyboard_triger() and pass_key_to_master(), this two use the idea that the variable master implements, having access to call methods out of scope.
Olso the method set_focused_object() stores a reference to the last object beeng focused, note than it stores the widget and not the event, it's easyer than searching each time the object
import tkinter as tk
from tkinter import ttk
class MainWindow(tk.Tk):
def keyboard_triger(self, key):
# to identify wath object is just use
# isinstance(self.active_input, ttk.Entry)
self.active_input.insert(tk.END, key)
def new_keyboard(self):
Keyboard(self)
def set_focused_object(self, event):
self.active_input = event.widget
def __init__(self):
tk.Tk.__init__(self)
self.active_input = None
ttk.Button(self, text="Show Keyboard", command=self.new_keyboard).pack()
self.input1 = ttk.Entry(self)
self.input1.bind("<FocusIn>", self.set_focused_object)
self.input1.pack()
self.input2 = ttk.Entry(self)
self.input2.bind("<FocusIn>", self.set_focused_object)
self.input2.pack()
self.input3 = tk.Text(self, height=3, width=15)
self.input3.bind("<FocusIn>", self.set_focused_object)
self.input3.pack()
class Keyboard(tk.Toplevel):
def pass_key_to_master(self, key):
self.master.keyboard_triger(key)
def __init__(self, master):
tk.Toplevel.__init__(self, master)
self.master = master
self.title('Keyboard')
# this way of agruping keys stores the kwags
# of the drawing method
keys = {
'A': {'x': 0, 'y': 0},
'B': {'x': 20, 'y': 20},
'C': {'x': 50, 'y': 50}
}
# expected structure
# {string key: reference to the button}
self.buttons = {}
for i in keys:
self.buttons[i] = tk.Button( # i=i is required to make a instance
self, text=i, command=lambda i=i: self.pass_key_to_master(i)
)
self.buttons[i].place(**keys[i])
if __name__ == '__main__':
root = MainWindow()
root.mainloop()
Your code maybe could have a better construction.(But I didn't revise your code construction.)
Followed by your code,I use a global variable.And fix some errors in your code.And it could work normally in my computer.
import tkinter as tk
from tkinter import ttk
class MainWindow(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.active_input = tk.Variable(value=None)
ttk.Button(self, text="show Keyboard", command=lambda: Keyboard(self)).pack()
global focusedWidget
focusedWidget = None
self.text = tk.StringVar(value="")
self.input1 = ttk.Entry(self)
self.input1.bind("<FocusIn>", self.getFocusWidget)
self.input2 = ttk.Entry(self)
self.input2.bind("<FocusIn>", self.getFocusWidget)
self.input3 = tk.Text(self, height=3, width=15)
self.input3.bind("<FocusIn>", self.getFocusWidget)
self.input1.pack()
self.input3.pack()
self.input2.pack()
def getFocusWidget(self,event): # this function could be a static function
global focusedWidget
focusedWidget = event.widget
class Keyboard(tk.Toplevel):
OPENED = False
NAME = "- Keyboard -"
NUM = [{"text":"1", "width":1},
{"text":"2", "width":1},
{"text":"3", "width":2}]
CHAR= [{"text":"A", "width":1},
{"text":"B", "width":1},
{"text":"C", "width":2}]
def __init__(self, master):
if not Keyboard.OPENED:
Keyboard.OPENED = True
print("keyboard opened!")
self.master = master
tk.Toplevel.__init__(self, master)
self.title(self.NAME)
self.protocol("WM_DELETE_WINDOW", self.close)
self.keyb_nb = ttk.Notebook(self)
self.keyb_nb.pack()
self.num_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.num_tab, Keyboard.NUM,2)
self.keyb_nb.add(self.num_tab, text="123")
self.char_tab = ttk.Frame(self.keyb_nb)
self.createPad(self.char_tab, Keyboard.CHAR, 2)
self.keyb_nb.add(self.char_tab, text="ABC")
def createPad(self, master, pad:list, max_col):
self.co_count = 0
self.ro = 1
for button in pad:
button["id"] = ttk.Button(master, width=6*button["width"], text=button["text"], command=lambda button=button:self.bclicked(button)) # this lambda expression has some errors.
if self.co_count >= max_col:
self.ro = self.ro + 1
self.co_count = 0
button["id"].grid(row=self.ro, columnspan=button["width"], column=self.co_count)
self.co_count = self.co_count+button["width"]
def bclicked(self, button:dict):
global focusedWidget
"""
reciver = self.master.active_input #I think the Problem here is, that the variable contains a string, not a widget
reciever.focus_force()
reciever.insert(index=tk.INSERT, string=button["text"])
"""
if not focusedWidget: # If user hasn't click a entry or text widget.
print("Please select a entry or text")
return
if focusedWidget.widgetName=='ttk::entry': # use if statement to check the type of selected entry.
focusedWidget.insert(index=tk.INSERT,string=button["text"])
else:
focusedWidget.insert("end",button["text"])
def close(self):
Keyboard.OPENED = False
self.destroy()
print("keyboard closed!")
root = MainWindow()
root.mainloop()

Python tkinter threading and window refresh

I'm using python and tkinter to build a visualization tool that can refresh and visualize an updating object. Right now, the object can't change because the threading is not working. Any help or general knowledge would be appreciated. I'm relatively new to threading and tkinter.
example object I want to ingest
class color1:
def __init__(self, color):
self.color = color
def change_col(self, new_color):
self.color = new_color
def pass_col(self):
return(self)
my visualization code
class my_visual(threading.Thread):
def __init__(self, col1):
threading.Thread.__init__(self)
self.start()
self.col1 = col1
def viz(self):
self.root = Tk()
btn1 = Button(self.root, text = 'Refresh', command = self.refresh)
btn1.pack()
frame = Frame(self.root, width = 100, height = 100, bg = self.col1.color)
frame.pack()
btn2 = Button(self.root, text = 'Close', command = self.exit)
btn2.pack()
self.root.mainloop()
def refresh(self):
self.root.quit()
self.root.destroy()
self.col1 = self.col1.pass_col()
self.viz()
def exit(self):
self.root.quit()
self.root.destroy()
Code that works
c = color1('RED')
test = my_visual(c)
test.viz()
Code that doesn't work
In this version, the refresh works, but the threading doesn't. When the threading is working, the refresh won't pick up that the object has changed.
c.change_col('BLUE')
If you extend the threading.Thread class you need to override the run() method with your custom functionality. With no run method, the thread dies immediately. You can test whether a thread is alive with my_visual.is_alive().
The problem is that your test.viz() is an infinite loop because of self.root.mainloop(), so you cannot do anything once you called that function. The solution is to use a thread for test.viz(), and your thread for the class my_visual is no more necessary.
I added a time.sleep of 2 seconds before the refresh makes it blue, otherwise the color is blue at beginning.
Here you go :
import threading
from tkinter import *
import time
class color1:
def __init__(self, color):
self.color = color
def change_col(self, new_color):
self.color = new_color
def pass_col(self):
return(self)
class my_visual():
def __init__(self, col1):
self.col1 = col1
def viz(self):
self.root = Tk()
btn1 = Button(self.root, text = 'Refresh', command = self.refresh)
btn1.pack()
frame = Frame(self.root, width = 100, height = 100, bg = self.col1.color)
frame.pack()
btn2 = Button(self.root, text = 'Close', command = self.exit)
btn2.pack()
self.root.mainloop()
def refresh(self):
self.root.quit()
self.root.destroy()
self.col1 = self.col1.pass_col()
print("self.col1", self.col1, self.col1.color)
self.viz()
def exit(self):
self.root.quit()
self.root.destroy()
c = color1('RED')
test = my_visual(c)
t2 = threading.Thread(target = test.viz)
t2.start()
time.sleep(2)
print('Now you can change to blue when refreshing')
c.change_col('BLUE')

Categories

Resources