Today I am trying to develop an UI using cefpython which allows me to embed a web browser and interacts with it with javascript bindings.
I'm using it to develop on Windows platform.
For this purpose, I am using the "multi_threaded_message_loop" flag which allows me to gain in performance.
I'm also using wxpython on python 3 to embed it.
The problem is when I resize my window, the use of WindowUtils.OnSize() freezes my app. 99% of the time, it happens when the browser is loading (but it also happens when it's done (rarely)).
Here is a sample code to reproduce :
import platform
import sys
import wx
from cefpython3 import cefpython
WindowUtils = cefpython.WindowUtils()
WIDTH = 800
HEIGHT = 600
import os
class MainFrame(wx.Frame):
browser = None
mainPanel = None
def createMainBrowser(self):
self.browser = self.createBrowser(self.mainPanel)
def createBrowser(self, parent):
browser = cefpython.CreateBrowserSync(
self.getWindowInfo(parent),
browserSettings={},
navigateUrl='http://www.google.com'
)
return browser
def getWindowInfo(self, parent):
windowInfo = cefpython.WindowInfo()
windowInfo.SetAsChild(parent.GetHandle(), [0, 0, WIDTH, HEIGHT])
return windowInfo
def __init__(self):
wx.Frame.__init__(
self, parent=None, id=wx.ID_ANY, title='wx', size=(WIDTH, HEIGHT)
)
self.mainPanel = wx.Panel(self)
self.mainPanel.SetBackgroundColour(wx.GREEN)
cefpython.PostTask(cefpython.TID_UI, self.createMainBrowser)
self.mainPanel.Bind(wx.EVT_SIZE, self.OnSize)
def OnSize(self, _):
if not self.browser:
return
WindowUtils.OnSize(self.mainPanel.GetHandle(), 0, 0, 0)
self.browser.NotifyMoveOrResizeStarted()
class App(wx.App):
def OnInit(self):
frame = MainFrame()
frame.Show()
return True
if __name__ == '__main__':
sys.excepthook = cefpython.ExceptHook # To shutdown all CEF processes on error
cefpython.Initialize({
"locales_dir_path": cefpython.GetModuleDirectory() + "/locales",
"browser_subprocess_path": cefpython.GetModuleDirectory() + "/subprocess",
"auto_zooming": "system_dpi",
"multi_threaded_message_loop": True,
})
app = App(False)
app.MainLoop()
cefpython.Shutdown()
Thank you a lot for your help !
Alann
Problem solved !
Instead of using
def OnSize(self, _):
if not self.browser:
return
WindowUtils.OnSize(self.mainPanel.GetHandle(), 0, 0, 0)
self.browser.NotifyMoveOrResizeStarted()
I use
def OnSize(self, sizeEvent):
if not self.browser:
return
w = sizeEvent.GetSize().GetWidth()
h = sizeEvent.GetSize().GetHeight()
win32gui.SetWindowPos(self.browser.GetWindowHandle(), 0, 0, 0, w, h, 0)
self.browser.NotifyMoveOrResizeStarted()
I don't know if this is because I'm on windows 10 but maybe WindowsUtils needs to be updated !
Related
I'm making a Curses music player UI with python and curses on windows 10. My program makes windows and renders them. This is the part of the code suspected to cause the flicker:
from ui_elements import *
from pynput.keyboard import Key, Listener
import curses
from curses import panel
def make_panel(x, y, width, height):
_panel = curses.newwin(height, width, y, x)
_panel = panel.new_panel(_panel)
_panel.top()
return _panel
class HomeScreen:
def __init__(self, parent):
self.parent = parent
self.dimensions = self.parent.parent.stdscr.getmaxyx()
# Make all the panels
self.panels = {
"side-navbar": make_panel(0, 0, 30, self.dimensions[0]),
"main-content": make_panel(30, 0, self.dimensions[1] - 30, self.dimensions[0])
}
self.listener = Listener(on_press=self.on_key_press)
self.listener.start()
self.num = 0
def on_key_press(self, key):
pass
def render(self):
self.dimensions = self.parent.parent.stdscr.getmaxyx()
win = self.panels["side-navbar"].window()
win.border()
win.addstr(1, 1, "Sidebar")
win.refresh()
win = self.panels["main-content"].window()
win.border()
win.addstr(1, 1, "Home")
win.refresh()
return ""
When I use STDSCR instead of creating a new screen, it works just fine.
But when I use another screen, this happens:
Any help would be highly appreciated.
I'm trying to get a USB camera to display a live video feed to a PyQt5 application. I have working code for a wx wrapper but I need this to work in PyQt5. After many searches I just can't find the right syntax.
Here's the working WX code:
import wx
from wx.lib.activexwrapper import MakeActiveXClass
from win32com.client import gencache
class mainFrm( wx.Frame ):
def __init__( self, *args, **kwds ):
kwds["style"] = wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__( self, *args, **kwds )
self.dcamModule = gencache.EnsureModule( '{6B9BD678-9710-44D9-9282-A088094E4216}', 0, 1, 0 )
dcamClass = MakeActiveXClass( self.dcamModule.ActiveGeni, eventObj = self )
self.camera = dcamClass( self, -1 )
self.camera.SetSize(( 752, 480 ))
self.SetClientSize( ( 752, 480 ))
self.camera.Acquire = True
self.camera.Display = True
if __name__ == '__main__':
GUI = wx.PySimpleApp( 0 )
frame_1 = mainFrm( None, -1, "" )
GUI.SetTopWindow( frame_1 )
frame_1.Show()
GUI.MainLoop()
When I debug what is happening this is what I get as the objects are built:
print(self.dcamModule)
<module 'win32com.gen_py.6B9BD678-9710-44D9-9282-A088094E4216x0x1x0' from '...\\AppData\\Local\\Temp\\3\\gen_py\\3.5\\6B9BD678-9710-44D9-9282-A088094E4216x0x1x0.py'>
print(dcamClass)
<class 'wx.lib.activexwrapper.AXControl_ActiveGeni'>
print(self.camera)
<win32com.gen_py.None.AXControl_ActiveGeni>
Here is the PyQt5 that I've tried. Is doesn't give an error but it doesn't start the camera either:
import sys
from PyQt5 import uic, QtWidgets
from PyQt5.QAxContainer import QAxWidget
qtCreatorFile = "ui\\camera_form.ui"
LandingPageUI, LandingPageBase = uic.loadUiType(qtCreatorFile)
class cameraForm(LandingPageBase, LandingPageUI):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self)
LandingPageBase.__init__(self)
self.setupUi(self)
self.ocx = QAxWidget("'{6B9BD678-9710-44D9-9282-A088094E4216}', 0, 1, 0 ")
#Is there something else to do here?
self.ocx.Acquire = True
self.ocx.Display = True
self.axWidget = self.ocx #axWidget is the QaXWidget on the form
if __name__ == "__main__":
app=QtWidgets.QApplication.instance()
if not app:
app = QtWidgets.QApplication(sys.argv)
window = cameraForm()
window.show()
sys.exit(app.exec_())
When I try the PyQt version this is what I get when debugging:
print(self.axWidget)
<PyQt5.QAxContainer.QAxWidget object at 0x036C4C60>
It seems that the MakeActiveXClass step for wx is doing something that isn't done with PyQt but I can't figure out what it should be instead.
Here are some resources that I've referenced so far:
win32.Dispatch vs win32.gencache in Python. What are the pros and cons?
What can you do with COM/ActiveX in Python?
I've tried QCamera as well but it does not recognize the camera.
Making self.axWidget = self.ocx does not cause self.ocx to replace self.axWidget in the window, the solution is to use self.axWidget by setting the control using the setControl() method:
class cameraForm(LandingPageBase, LandingPageUI):
def __init__(self, parent=None):
super(cameraForm, self).__init__(parent)
self.setupUi(self)
self.axWidget.setControl("{6B9BD678-9710-44D9-9282-A088094E4216}")
self.axWidget.setProperty("Acquire", True)
self.axWidget.setProperty("Display", True)
(Code not tested)
Got it to work by finding the right call in the setControl. Not sure why the CLSID didn't work but this did:
self.axWidget.setControl("ActiveGeni.ActiveGeni")
I'm excited to use this feature in Qt but I'm still not sure how to see what other activeX I can call. For example I could use "Microsoft Web Browser" and load a PDF but not "Adobe PDF Reader". How do I see what is available?
I ran into a problem with ScreenDC in wxPython Phoenix.
My tool is supposed to take multiple screenshots with some period. But whenever I use ScreenDC to grab a screenshot and save it to PNG it works well only for the first time. All the following times it just saves the same image as the first one. To get a new image, I have to restart the program, which is not an option in my case. I guess that whenever I call wx.ScreenDC() it gets the same image as the first time.
Ubuntu 16.04, wxPython 3.0.3 gtk3, python 3.6
The code I used:
def take_screenshot():
screen = wx.ScreenDC()
size = screen.GetSize()
width = size[0]
height = size[1]
bmp = wx.Bitmap(width, height)
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, width, height, screen, 0, 0)
bmp.SaveFile(str(datetime.now()) + '.png', wx.BITMAP_TYPE_PNG)
if __name__ == '__main__':
app = wx.App()
take_screenshot()
sleep(3)
take_screenshot()
sleep(3)
take_screenshot()
sleep(3)
take_screenshot()
Maybe there is the way to clean that first image from memory.
The only solution I found is to run a separate process, define wx.App inside and then to perform the function. However, that is not an option for my program.
Thanks.
UPD: It seems to be some issue of wxPython Phoenix. If you run this on wxPython Classic, everything works fine(just use EmptyBitmap, not Bitmap). Weird, I will report this issue in their repository.
I was not able to reproduce your issue in Phoenix or Classic (on Windows). I suppose what could happen is that sleep blocks wxPython event loop. It would be good style to put long-running things in a separate thread anyway. It is painless, see below.
from threading import Thread
...
if __name__ == '__main__':
app = wx.App()
def payload():
take_screenshot()
sleep(3)
take_screenshot()
sleep(3)
take_screenshot()
sleep(3)
take_screenshot()
thrd = Thread(target=payload)
thrd.start()
EDIT: As the asker pointed out, there may be issues with thread-safety in the approach above. How does the thing work below for you (tested on Phoenix and Classic on Windows)?
from __future__ import print_function
import wx
from datetime import datetime
from time import sleep
IS_PHOENIX = True if 'phoenix' in wx.version() else False
if IS_PHOENIX:
EmptyBitmap = lambda *args, **kwds: wx.Bitmap(*args, **kwds)
else:
EmptyBitmap = lambda *args, **kwds: wx.EmptyBitmap(*args, **kwds)
def take_screenshot():
screen = wx.ScreenDC()
size = screen.GetSize()
width = size[0]
height = size[1]
bmp = EmptyBitmap(width, height)
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, width, height, screen, 0, 0)
bmp.SaveFile(str(datetime.now().second) + '.png', wx.BITMAP_TYPE_PNG)
MAXPICS = 4
class testfrm(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.tmr = wx.Timer(self, -1)
self.countpics = 0
self.Bind(wx.EVT_TIMER, self.ontimer, self.tmr)
self.ontimer(None)
def ontimer(self, evt):
if self.countpics <=MAXPICS:
self.tmr.Start(3000, wx.TIMER_ONE_SHOT)
take_screenshot()
self.countpics += 1
else:
self.Close()
if __name__ == '__main__':
app = wx.App()
frm = testfrm(None, -1, wx.version())
app.MainLoop()
I am trying to display a grid of images in wxPython. I am using a GridSizer to create a grid to which I add my staticbitmaps. But for some reason I only see one image at the first position in the grid. I am not sure where I am going wrong. Here is my code and the corresponding output.
import wx
import sys
import glob
MAIN_PANEL = wx.NewId()
class CommunicationApp(wx.App):
"""This is the class for the communication tool application.
"""
def __init__(self, config=None, redirect=False):
"""Instantiates an application.
"""
wx.App.__init__(self, redirect=redirect)
self.cfg = config
self.mainframe = CommunicationFrame(config=config,
redirect=redirect)
self.mainframe.Show()
def OnInit(self):
# self.SetTopWindow(self.mainframe)
return True
class CommunicationFrame(wx.Frame):
"""Frame of the Communication Application.
"""
def __init__(self, config, redirect=False):
"""Initialize the frame.
"""
wx.Frame.__init__(self, parent=None,
title="CMC Communication Tool",
style=wx.DEFAULT_FRAME_STYLE)
self.imgs = glob.glob('../img/img*.png')
self.panel = CommuniationPanel(parent=self,
pid=MAIN_PANEL,
config=config)
# # Gridbagsizer.
nrows, ncols = 3, 4
self.grid = wx.GridSizer(rows=nrows, cols=ncols)
# Add images to the grid.
for r in xrange(nrows):
for c in xrange(ncols):
_n = ncols * r + c
_tmp = wx.Image(self.imgs[_n],
wx.BITMAP_TYPE_ANY)
_temp = wx.StaticBitmap(self.panel, wx.ID_ANY,
wx.BitmapFromImage(_tmp))
self.grid.Add(_temp, 0, wx.EXPAND)
self.grid.Fit(self)
# set to full screen.
# self.ShowFullScreen(not self.IsFullScreen(), 0)
class CommuniationPanel(wx.Panel):
"""Panel of the Communication application frame.
"""
def __init__(self, parent, pid, config):
"""Initialize the panel.
"""
wx.Panel.__init__(self, parent=parent, id=pid)
# CALLBACK BINDINGS
# Bind keyboard events.
self.Bind(wx.EVT_KEY_UP, self.on_key_up)
def on_key_up(self, evt):
"""Handles Key UP events.
"""
code = evt.GetKeyCode()
print code, wx.WXK_ESCAPE
if code == wx.WXK_ESCAPE:
sys.exit(0)
def main():
app = CommunicationApp()
app.MainLoop()
if __name__ == '__main__':
main()
Here is the output.
What I would like to see (or expected to see) was 3X4 grid of the different images.
What is the problem with my code? And how do I fix it?
You appear to have forgotten to set a sizer for your panel
Try putting this in the frame's __init__
self.panel.SetSizer(self.grid)
I'm facing a problem with python.
i want to draw on my monitor a circle, that can move around.
let's say i have my browser open, i want to be able to make the circle go around on his own AND be able to use the mouse to press any button i want.
the idea is that the circle is connected to my hands movement thanks to Leap Motion, and i want to display the gestures i make while being able to use the mouse.
my worries are that i have to make a trasparent window which doesn't let me to use the mouse because i would clik on the trasparent window.
Thanks!
Found the solution, i used wxPython
Down below the code:
import wx, inspect, os, sys, win32gui
IMAGE_PATH = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + '\cerchio.png'
class Cursor(wx.Frame):
def __init__(self, parent, log):
self.log = log
self.delta = wx.Point(0,0)
wx.Frame.__init__(self, parent, -1, "Shaped Window",
style =
wx.FRAME_SHAPED
| wx.SIMPLE_BORDER
| wx.FRAME_NO_TASKBAR
| wx.STAY_ON_TOP
)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.update, self.timer)
self.Bind(wx.EVT_PAINT, self.OnPaint)
self.x=0
self.y=0
self.hasShape = False
self.SetClientSize( (50, 50) )
image = wx.Image(IMAGE_PATH, wx.BITMAP_TYPE_PNG)
image.SetMaskColour(255,255,255)
image.SetMask(True)
self.bmp = wx.BitmapFromImage(image)
self.SetWindowShape()
self.timer.Start(1)
dc = wx.ClientDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def SetWindowShape(self, *evt):
# Use the bitmap's mask to determine the region
r = wx.RegionFromBitmap(self.bmp)
self.hasShape = self.SetShape(r)
def OnPaint(self, evt):
dc = wx.PaintDC(self)
dc.DrawBitmap(self.bmp, 0, 0, True)
def OnExit(self, evt):
self.Close()
def update(self, event):
#self.x, self.y = win32gui.GetCursorPos()
self.SetPosition((self.x,self.y))
if __name__ == '__main__':
app = wx.App( False )
frm = Cursor(None, -1)
frm.Show()
app.MainLoop()
This maps 50x50 png image with white background (modify IMAGE_PATH) to the cursor position (you won't be able to click though)