I was trying to get fullscreen support for a wxPython app using the code in the answer from this stackoverflow question wxPython MacOS X Lion full screen mode
My Error
Traceback (most recent call last):
File "test_mac_fullscreen.py", line 36, in <module>
frame = Frame()
File "test_mac_fullscreen.py", line 29, in __init__
SetFullScreenCapable(self)
File "test_mac_fullscreen.py", line 16, in SetFullScreenCapable
window = frameobj.window()
AttributeError: 'NSHIObject' object has no attribute 'window'
My Code (just copied and pasted into one file from the above link)
# from https://stackoverflow.com/questions/12328143/getting-pyobjc-object-from-integer-id
import ctypes, objc
_objc = ctypes.PyDLL(objc._objc.__file__)
# PyObject *PyObjCObject_New(id objc_object, int flags, int retain)
_objc.PyObjCObject_New.restype = ctypes.py_object
_objc.PyObjCObject_New.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int]
def objc_object(id):
return _objc.PyObjCObject_New(id, 0, 1)
def SetFullScreenCapable(frame):
frameobj = objc_object(frame.GetHandle())
NSWindowCollectionBehaviorFullScreenPrimary = 1<<7
window = frameobj.window()
newBehavior = window.collectionBehavior() | NSWindowCollectionBehaviorFullScreenPrimary
window.setCollectionBehavior_(newBehavior)
import wxversion
wxversion.select('2-osx_cocoa') # require Cocoa version of wxWidgets
import wx
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.Bind(wx.EVT_CLOSE, self.OnClose)
wx.Button(self, label="Hello!") # test button to demonstrate full-screen resizing
SetFullScreenCapable(self)
def OnClose(self, event):
print "Closing"
exit()
if __name__ == "__main__":
app = wx.App(False)
frame = Frame()
frame.Show()
app.MainLoop()
print "running app loop"
While this is rather late, recently looking at this it suddenly clicked.
If you notice in the error it states a class NSHIObject (HI I am guessing stands for Human Interface) this has to do with the backend that wxPython uses, the archaic Carbon (as in this case) or the updated Cocoa. In earlier versions only Carbon was available but with 2.9.5 (IIRC) Cocoa is available (and I believe it has sense moved to 3.0 with Cocoa or Carbon ). Simply reinstall with a cocoa version and it works.
Related
I'm developing an GUI for multi-robot system using ROS, but i'm freezing in the last thing i want in my interface: embedding the RVIZ, GMAPPING or another screen in my application. I already put an terminal in the interface, but i can't get around of how to add an external application window to my app. I know that PyQt5 have the createWindowContainer, with uses the window ID to dock an external application, but i didn't find any example to help me with that.
If possible, i would like to drag and drop an external window inside of a tabbed frame in my application. But, if this is not possible or is too hard, i'm good with only opening the window inside a tabbed frame after the click of a button.
I already tried to open the window similar to the terminal approach (see the code bellow), but the RVIZ window opens outside of my app.
Already tried to translate the attaching/detaching code code to linux using the wmctrl command, but didn't work wither. See my code here.
Also already tried the rviz Python Tutorial but i'm receveing the error:
Traceback (most recent call last):
File "rvizTutorial.py", line 23, in
import rviz
File "/opt/ros/indigo/lib/python2.7/dist-packages/rviz/init.py", line 19, in
import librviz_shiboken
ImportError: No module named librviz_shiboken
# Frame where i want to open the external Window embedded
self.Simulation = QtWidgets.QTabWidget(self.Base)
self.Simulation.setGeometry(QtCore.QRect(121, 95, 940, 367))
self.Simulation.setTabPosition(QtWidgets.QTabWidget.North)
self.Simulation.setObjectName("Simulation")
self.SimulationFrame = QtWidgets.QWidget()
self.SimulationFrame.setObjectName("SimulationFrame")
self.Simulation.addTab(rviz(), "rViz")
# Simulation Approach like Terminal
class rviz(QtWidgets.QWidget):
def __init__(self, parent=None):
super(rviz, self).__init__(parent)
self.process = QtCore.QProcess(self)
self.rvizProcess = QtWidgets.QWidget(self)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.rvizProcess)
# Works also with urxvt:
self.process.start('rViz', [str(int(self.winId()))])
self.setGeometry(121, 95, 940, 367)
I've not tested this specifically, as I've an old version of Qt5 I can't upgrade right now, while from Qt5 5.10 startDetached also returns the pid along with the bool result from the started process.
In my tests I manually set the procId (through a static QInputBox.getInt()) before starting the while cycle that waits for the window to be created.
Obviously there are other ways to do this (and to get the xid of the window).
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck, Gdk
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.embed('xterm')
def embed(self, command, *args):
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
started, procId = proc.startDetached()
if not started:
QtWidgets.QMessageBox.critical(self, 'Command "{}" not started!')
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
if w.get_pid() == procId:
window = QtGui.QWindow.fromWinId(w.get_xid())
container = QtWidgets.QWidget.createWindowContainer(window, self)
self.addTab(container, command)
return
attempts += 1
QtWidgets.QMessageBox.critical(self, 'Window not found', 'Process started but window not found')
app = QtWidgets.QApplication(sys.argv)
w = Container()
w.show()
sys.exit(app.exec_())
I couldn't get the code in the accepted answer to work on Ubuntu 18.04.3 LTS; even when I got rid of the exceptions preventing the code to run, I'd still get a separate PyQt5 window, and separate xterm window.
Finally after some tries, I got the xterm window to open inside the tab; here is my code working in Ubuntu 18.04.3 LTS (with all the misses commented):
#!/usr/bin/env python3
# (same code seems to run both with python3 and python2 with PyQt5 in Ubuntu 18.04.3 LTS)
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import gi
gi.require_version('Wnck', '3.0')
from gi.repository import Wnck, Gdk
import time
class Container(QtWidgets.QTabWidget):
def __init__(self):
QtWidgets.QTabWidget.__init__(self)
self.embed('xterm')
def embed(self, command, *args):
proc = QtCore.QProcess()
proc.setProgram(command)
proc.setArguments(args)
#started, procId = proc.startDetached()
#pid = None
#started = proc.startDetached(pid)
# https://stackoverflow.com/q/31519215 : "overload" startDetached : give three arguments, get a tuple(boolean,PID)
# NB: we will get a failure `xterm: No absolute path found for shell: .` even if we give it an empty string as second argument; must be a proper abs path to a shell
started, procId = proc.startDetached(command, ["/bin/bash"], ".")
if not started:
QtWidgets.QMessageBox.critical(self, 'Command "{}" not started!'.format(command), "Eh")
return
attempts = 0
while attempts < 10:
screen = Wnck.Screen.get_default()
screen.force_update()
# do a bit of sleep, else window is not really found
time.sleep(0.1)
# this is required to ensure that newly mapped window get listed.
while Gdk.events_pending():
Gdk.event_get()
for w in screen.get_windows():
print(attempts, w.get_pid(), procId, w.get_pid() == procId)
if w.get_pid() == procId:
self.window = QtGui.QWindow.fromWinId(w.get_xid())
#container = QtWidgets.QWidget.createWindowContainer(window, self)
proc.setParent(self)
#self.scrollarea = QtWidgets.QScrollArea()
#self.container = QtWidgets.QWidget.createWindowContainer(self.window)
# via https://vimsky.com/zh-tw/examples/detail/python-method-PyQt5.QtCore.QProcess.html
#pid = proc.pid()
#win32w = QtGui.QWindow.fromWinId(pid) # nope, broken window
win32w = QtGui.QWindow.fromWinId(w.get_xid()) # this finally works
win32w.setFlags(QtCore.Qt.FramelessWindowHint)
widg = QtWidgets.QWidget.createWindowContainer(win32w)
#self.container.layout = QtWidgets.QVBoxLayout(self)
#self.addTab(self.container, command)
self.addTab(widg, command)
#self.scrollarea.setWidget(self.container)
#self.container.setParent(self.scrollarea)
#self.scrollarea.setWidgetResizable(True)
#self.scrollarea.setFixedHeight(400)
#self.addTab(self.scrollarea, command)
self.resize(500, 400) # set initial size of window
return
attempts += 1
QtWidgets.QMessageBox.critical(self, 'Window not found', 'Process started but window not found')
app = QtWidgets.QApplication(sys.argv)
w = Container()
w.show()
sys.exit(app.exec_())
i just started to learn PyQt and GUI programing and
i copied this code exactly from the book "Rapid GUI Programming with Python and Qt:The Definitive Guide to PyQt Programming" and it supposed to show a calculator that calculates an expression.
when i run the application main window shows up but does not do anything and since i copied the code form a well known pyqt book it's very strange.
i am using python 3.4.4 and pyqt4 .this is the code i copied from book:
import sys
from math import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Form(QDialog):
def __init__(self,parent=None):
super(Form, self).__init__(parent)
self.browser = QTextBrowser()
self.lineedit = QLineEdit("Type an expression and press Enter")
self.lineedit.selectAll()
layout = QVBoxLayout()
layout.addWidget(self.browser)
layout.addWidget(self.lineedit)
self.setLayout(layout)
self.lineedit.setFocus()
self.connect(self.lineedit, SIGNAL("retrunPressed()"),
self.updateUi)
self.setWindowTitle("Calculate")
def updateUi(self):
try:
text= unicode(self.lineedit.text())
self.browser.append("{0} = <b>{1}</b>".format(text,eval(text)))
except:
self.browser.append(
"<font color=red>%s is invalid!</font>" % text)
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
these are the errors i get:
Traceback (most recent call last):
File "calculator.pyw", line 25, in updateUi
text = unicode(self.lineedit.text())
NameError: name 'unicode' is not defined
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "calculator.pyw", line 29, in updateUi
"%s is invalid!" % text)
UnboundLocalError: local variable 'text' referenced before assignment
i know its not good idea to ask someone else to debug my code but i did all i could about it and nothing came up.
thanks
You have a typo in your signal name it should be
self.connect(self.lineedit, SIGNAL("returnPressed()"),
not
self.connect(self.lineedit, SIGNAL("retrunPressed()"),
so apparently you didn't copy it well, also it seems that book was written in python2 so you don't need the unicode function strings in python3 are already unicode by default
I ran the following python code
import wx
class myframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, -1, title="Hello")
self.InitUI()
def InitUI(self):
menubar = wx.MenuBar()
fileMenu = wx.Menu()
qmi = wx.MenuItem(fileMenu, 100, '&Quit\tCtrl+Q')
qmi.SetBitmap(wx.Image(
'quit.png', wx.BITMAP_TYPE_ANY).ConvertToBitmap())
fileMenu.AppendItem(qmi)
self.Bind(wx.EVT_MENU, self.OnQuit, id=100)
menubar.Append(fileMenu, '&File')
self.SetMenuBar(menubar)
self.SetSize((250, 200))
self.SetTitle('Icons and shortcuts')
self.Centre()
self.Show(True)
def OnQuit(self, e):
self.Close()
def main():
ex = wx.App()
myframe()
ex.MainLoop()
if __name__ == '__main__':
main()
The above code is throwing the message
qmi.SetBitmap(wx.Image('quit.png', wx.BITMAP_TYPE_ANY).ConvertToBitmap())
File "C:\Python27\lib\site-packages\wx-3.0-msw\wx\_core.py", line 2882, in __init__
_core_.Image_swiginit(self,_core_.new_Image(*args, **kwargs))
PyAssertionError: C++ assertion "strcmp(setlocale(LC_ALL, NULL), "C") == 0" failed at ..\..\src\common\intl.cpp(1449) in wxLocale::GetInfo(): You probably called setlocale() directly instead of using wxLocale and now there is a mismatch between C/C++ and Windows locale.
Things are going to break, please only change locale by creating wxLocale objects to avoid this!
I am new to python and completely unable to solve this.
Is there anyway to find the solution to this.
I am using python 2.7.10 and wxpython 3.0.2.0 in a windows machine.
I had a similar issue that only occurred following a cxFreeze with wxPhoenix. I worried that the solution above could cause issues on a machine that wasn't wx.LANGUAGE_ENGLISH, so instead, I recorded the locale before any imports (which I suspected were changing the locale):
import wx
locale = wx.Locale.GetSystemLanguage()
And then reset it after my imports and after creating the app:
app = wx.App(redirect=False)
app.locale = wx.Locale(locale)
This worked for me.
The solution is to modify the locale and it is working.
Modified the code as here:
def main():
ex = wx.App()
ex.locale = wx.Locale(wx.LANGUAGE_ENGLISH)
myframe()
ex.MainLoop()
Locale is conflicting with system locale. So modified it in the code for this program.
This was fixed if you included some wx.App code:
def InitLocale(self):
import sys
if sys.platform.startswith("win") and sys.version_info > (3, 8):
import locale
locale.setlocale(locale.LC_ALL, "C")
Also, it was fixed correctly in wx4.2 which was just released. If you hit this bug, it was never you. wx and python were disagreeing about the locale. It hits intermittently and only for some non-English locale. 4.2 fixes, or the above code snipple to get rid of the local for post py3.8.
I've made a gui in glade that I want to put in a python program. I was adapting the instructions from a tutorial I found online to load in my glade file (http://www.pygtk.org/articles/pygtk-glade-gui/Creating_a_GUI_using_PyGTK_and_Glade.htm). When I had problems I tried something basic (one button) calling it the same thing as in that tutorial, and copy pasting their code, and it still didn't work. I also took a look at (http://www.linuxjournal.com/article/6586?page=0,2), which has a function being called slightly differently ("self.wTree=gtk.glade.XML (gladefile,windowname)" instead of without windowname), and implemented an equivalent with mine and that didn't fix it. I definitely have pygtk working, I made something without using glade before and it worked fine. The error I'm getting is:
/usr/share/themes/NOX/gtk-2.0/gtkrc:233: Murrine configuration option "gradients"
is no longer supported and will be ignored.
(helloWorld.py:9804): libglade-WARNING **: Expected <glade-interface>. Got
<interface>.
(helloWorld.py:9804): libglade-WARNING **: did not finish in PARSER_FINISH state
Traceback (most recent call last):
File "helloWorld.py", line 31, in <module>
hwg = HellowWorldGTK()
File "helloWorld.py", line 22, in __init__
self.wTree = gtk.glade.XML(self.gladefile)
RuntimeError: could not create GladeXML object
I'm running xubuntu 11.04. The Murrine configuration thing comes up when any gtk application opens, but I included it in case it is relevant. Here's the code I took from the tutorial (but isn't working)
#!/usr/bin/env python
import sys
try:
import pygtk
pygtk.require("2.0")
except:
pass
try:
import gtk
import gtk.glade
except:
sys.exit(1)
class HellowWorldGTK:
"""This is an Hello World GTK application"""
def __init__(self):
#Set the Glade file
self.gladefile = "PyHelloWorld.glade"
self.wTree = gtk.glade.XML(self.gladefile)
#Get the Main Window, and connect the "destroy" event
self.window = self.wTree.get_widget("MainWindow")
self.window.show()
if (self.window):
self.window.connect("destroy", gtk.main_quit)
if __name__ == "__main__":
hwg = HellowWorldGTK()
gtk.main()
Try with this code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
class HellowWorldGTK:
def __init__(self):
self.gladefile = "helloworld.glade"
self.glade = gtk.Builder()
self.glade.add_from_file(self.gladefile)
self.glade.connect_signals(self)
self.glade.get_object("MainWindow").show_all()
def on_MainWindow_delete_event(self, widget, event):
gtk.main_quit()
if __name__ == "__main__":
try:
a = HellowWorldGTK()
gtk.main()
except KeyboardInterrupt:
pass
Remember:
In Glade, Edit the "Preferences" of the file to "GTKBuilder" (not "libglade")
Your PyHelloWorld.glade is incorrect. Make sure you created it with the correct Glade application, there are Glade2 and Glade3 applications that can be installed and used. If you downloaded the file make sure it is correct. The error message says it all:
Expected <glade-interface>. Got <interface>
So the XML file has the interface tag, but PyGTK library expects glade-interface tag.
Since I always end up having problems with this, here is a Python 2.7 code that I use for one or the other:
for Libglade:
# needs libglade (not for gtk-builder)
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
gladefile = "test-libglade.glade"
wTree = gtk.glade.XML(gladefile)
window = wTree.get_widget("MainWindow")
if (window):
window.connect("destroy", gtk.main_quit)
window.show_all() # must have!
gtk.main()
For GtkBuilder:
# needs gtk-builder (not for libglade)
import pygtk
pygtk.require("2.0")
import gtk
gladefile = "test-gtkbuilder.glade"
wTree = gtk.Builder()
wTree.add_from_file(gladefile)
window = wTree.get_object("MainWindow")
if (window):
window.connect("destroy", gtk.main_quit)
window.show_all() # must have!
gtk.main()
In Glade, you can just add a Window, call it MainWindow, and save two versions with the respective filenames as above for each format; and these snippets should work with them respeactively.
Hope this helps someone,
Cheers!
This works perfectly.
#!/usr/bin/python
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
class SubinsWindow:
def __init__(self):
self.gladefile = "game.glade"
self.glade = gtk.Builder()
self.glade.add_from_file(self.gladefile)
self.glade.connect_signals(self)
self.win=self.glade.get_object("window1") # Window Name in GLADE
self.win.show_all()
if __name__ == "__main__":
a = SubinsWindow()
gtk.main()
If you are using GTK+3 in python, see builder.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class Handler:
def onDestroy(self, *args):
Gtk.main_quit()
def onButtonPressed(self, button):
print("Hello World!")
builder = Gtk.Builder()
builder.add_from_file("builder_example.glade")
builder.connect_signals(Handler())
window = builder.get_object("window1")
window.show_all()
Gtk.main()
For the following simple wxPython snippets:
import sys
import wx
class MyApp(wx.App):
def OnInit(self):
self.frame = wx.Frame(None, title="Simple wxPython App")
self.frame.Show()
self.SetTopWindow(self.frame)
return True
def main(argv=sys.argv[:]):
app = MyApp()
app.MainLoop()
return 0
if __name__ == '__main__':
sys.exit(main())
I always got the warning message "R0904: 12:MyApp: Too many public methods" from Pylint. How can I prevent that?
# pylint: disable=R0904
Stick that at the top of the offending class.
On older versions of Pylint, you have to use
# pylint: disable-msg=R0904
Unfortunately, if you ever upgrade to a more recent version you'll have to write a sed script to replace all instances of # pylint: disable-msg with # pylint: disable.