I am writing a program on Python 3.4 that uses GTK Map widget (OsmGpsMap) and pygame for joystick input.
Here is a simple program that displays a window with a map:
import gi
from gi.repository import Gtk
from gi.repository import OsmGpsMap
window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
window.connect('destroy', Gtk.main_quit)
map_widget = OsmGpsMap.Map()
window.add(map_widget)
window.show_all()
Gtk.main()
But if I just add a
import pygame
line (without pygame.init() and etc.), the program doesnt launch and I receive a message that python.exe have stopped.
So, how is it possible to fix that?
Do not mix frameworks. The frameworks may interact poorly with each other or conflict completely. If it works on your (operating) system, that doesn't mean it will work on another (operating) system or with a different version of one of the frameworks. Mixing frameworks always means some kind of undefined behavior.
Pygame is based on SDL2, so there is no chance that PyGame can display an osmgpsmap which is a Gtk+ widget.
Related
Note how this application implements a mainloop by the activate signal and .run() method call:
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk
def on_activate(app):
win = Gtk.ApplicationWindow(application=app)
btn = Gtk.Button(label="Hello, World!")
btn.connect('clicked', lambda x: win.close())
win.set_child(btn)
win.present()
app = Gtk.Application(application_id='org.gtk.Example')
app.connect('activate', on_activate)
app.run(None)
Correct me: I think this activate signal is emitted as an event to synchronise the creation of GTK windows, widgets, etc, so that the GUI part does not race to happen before the application is ready. What else is the point?
Then, when the Window, widgets, etc are all drawn, the application gets stuck in a mainloop by .run(). This way the application doesn't exit immediately, but rather waits for user input.
What bothers me here is that I don't see the value of the Application class. Because of it, we got an event on signal activate to tell "oh the app is ready", but what is this app anyway? Why can't I just do .run() on Gtk.ApplicationWindow or even Gtk.Window?
In order to answer those questions above, I invented/discovered this question that, if answered, I think it will address my frustration:
Question: What is the GT4 approach in running GUI apps without using Application or ApplicationWindow classes?
Background
I come from a TKinter background. There, I could make an app simply by saying something like:
import tkinter
class MyApp(tkinter.Tk):
def __init__(self):
super().__init__()
self.title('some title')
if __name__ == '__main__':
myapp = MyApp()
myapp.mainloop()
It feels much simpler, without a spaghetti of creating two objects (like in GTK4) with signals emitting through them just to barely get a .run() mainloop.
But I have to use GTK4 for this project, and Tkinter is not an option. So I have to learn the suitable GTK4 for me.
According to the docs: "GtkApplication is a high-level API for writing applications.
It supports many aspects of writing a GTK application in a convenient fashion, without enforcing a one-size-fits-all model.
Currently, GtkApplication handles GTK initialization, application uniqueness, session management, provides some basic scriptability and desktop shell integration by exporting actions and menus and manages a list of toplevel windows whose life-cycle is automatically tied to the life-cycle of your application.
While GtkApplication works fine with plain GtkWindows, it is recommended to use it together with GtkApplicationWindow."
Source: https://docs.gtk.org/gtk4/class.Application.html
The clasic hello world from Gtk3 will not work on Gtk4
This works:
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
win = Gtk.Window()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
But if you change "3.0" by "4.0" does not work and the reason is on the migration guide:
"GTK 4 removes the gtk_main_* family of APIs. The recommended replacement is GtkApplication"
https://docs.gtk.org/gtk4/migrating-3to4.html#stop-using-gtk_main-and-related-apis
Gtk have a different approach to Tk, you will need to be used to that.
And GTK4 is still new, you will found more examples about Gtk3 code, so, keep the migration guide at hand
Is there any way to control where pygame creates the game screen? It seems to always create it in the same general area but not in a consistent location.
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])
as per http://pygame.org/wiki/FrequentlyAskedQuestions
You can also just center the screen with
import pygame, os
os.environ['SDL_VIDEO_CENTERED'] = '1'
Note that these should be done before you initialize pygame in the main loop. I do it right after I import os for example. And since they are not actually part of pygame, you can probably use it elsewhere, though things like gtk and wxpython provide their own mechanisms.
Positioning of windows is not handled by the client application. It's handled by the Window manager (metacity etc.).
The SDL library on which PyGame is based does have a few environment variables which can be used to give hints to the Window manager. These are hints which the WM may ignore but it's the best you can do.
The comments over here have an example.
Is there any way to control where pygame creates the game screen? It seems to always create it in the same general area but not in a consistent location.
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = str(position[0]) + "," + str(position[1])
as per http://pygame.org/wiki/FrequentlyAskedQuestions
You can also just center the screen with
import pygame, os
os.environ['SDL_VIDEO_CENTERED'] = '1'
Note that these should be done before you initialize pygame in the main loop. I do it right after I import os for example. And since they are not actually part of pygame, you can probably use it elsewhere, though things like gtk and wxpython provide their own mechanisms.
Positioning of windows is not handled by the client application. It's handled by the Window manager (metacity etc.).
The SDL library on which PyGame is based does have a few environment variables which can be used to give hints to the Window manager. These are hints which the WM may ignore but it's the best you can do.
The comments over here have an example.
I know to embed vim in a Gtk application using sockets like the following snippet
from gi.repository import Gtk
import subprocess
win=Gtk.Window()
win.set_default_size(600,800)
win.connect('delete-event', Gtk.main_quit)
editor = Gtk.Socket()
win.add(editor)
editor.connect("plug-removed", Gtk.main_quit)
subprocess.Popen(["/usr/bin/gvim", \
"--socketid", str(editor.get_id())])
win.show_all()
Gtk.main()
How does one do this in PySide? I could not find any reference to sockets in pyside.
UPDATE (using JimP's idea)
The following code embeds a gvim instance in a Pyside widget. However the gvim window does not seem to resize when to the full size of the parent window.
import sys
from PySide import QtGui
from PySide import QtCore
app = QtGui.QApplication(sys.argv)
win = QtGui.QWidget()
win.resize(600, 800)
container = QtGui.QX11EmbedContainer(win)
container.show()
QtCore.QObject.connect(container,
QtCore.SIGNAL("clientClosed()"),
QtCore.QCoreApplication.instance().quit)
winId = container.winId()
process = QtCore.QProcess(container)
options = ["--socketid", str(winId)]
process.start("gvim", options)
win.show()
sys.exit(app.exec_())
I think the key to getting this working would be translating GTK speak to QT speak. Google around your code, I see that Gtk.Socket says:
The communication between a GtkSocket and a GtkPlug follows the XEmbed
protocol. This protocol has also been implemented in other toolkits,
e.g. Qt, allowing the same level of integration when embedding a Qt
widget in GTK or vice versa.
So then the question becomes what does QT call their XEmbed classes? Google around I found QX11EmbedContainer which says:
It is possible for PySide.QtGui.QX11EmbedContainer to embed XEmbed
widgets from toolkits other than Qt, such as GTK+. Arbitrary
(non-XEmbed) X11 widgets can also be embedded, but the XEmbed-specific
features such as window activation and focus handling are then lost.
The GTK+ equivalent of PySide.QtGui.QX11EmbedContainer is GtkSocket.
The corresponding KDE 3 widget is called QXEmbed.
I'm not running PySide at the moment, but that page on QX11EmbedContainer contains some example C++ code that I think will get you where you need to be. You will need to translate the C++ to Python, but I don't that will be too hard.
What is the best way to use PyGame (SDL) within a PyGTK application?
I'm searching for a method that allows me to have a drawing area in the GTK window and at the same time being able to manage both GTK and SDL events.
I've never attempted it myself, but hearing plenty about other people who've tried, it's not a road you want to go down.
There is the alternative of putting the gui in pygame itself. There are plenty of gui toolkits built specifically for pygame that you could use. Most of them are rather unfinished, but there are 2 big, actively maintained ones: PGU and OcempGUI. The full list on the pygame site is here.
You may be interested in this message thread. Looks like they recommend against it.
PyGame works much better when it can manage its own window, or even better, use the whole screen. GTK has flexible enough widgets to allow creation of a drawing area.
This page may help, though, if you want to try it.
There's a simple solution that might work for you.
Write the PyGTK stuff and PyGame stuff as separate applications. Then from the PyGTK application call the PyGame application, using os.system to call the PyGame application. If you need to share data between the two then either use a database, pipes or IPC.
http://faq.pygtk.org/index.py?file=faq23.042.htp&req=show mentions it all:
You need to create a drawing area and set the environment variable SDL_WINDOWID after it's realized:
import os
import gobject
import gtk
import pygame
WINX = 400
WINY = 200
window = gtk.Window()
window.connect('delete-event', gtk.main_quit)
window.set_resizable(False)
area = gtk.DrawingArea()
area.set_app_paintable(True)
area.set_size_request(WINX, WINY)
window.add(area)
area.realize()
# Force SDL to write on our drawing area
os.putenv('SDL_WINDOWID', str(area.window.xid))
# We need to flush the XLib event loop otherwise we can't
# access the XWindow which set_mode() requires
gtk.gdk.flush()
pygame.init()
pygame.display.set_mode((WINX, WINY), 0, 0)
screen = pygame.display.get_surface()
image_surface = pygame.image.load('foo.png')
screen.blit(image_surface, (0, 0))
gobject.idle_add(pygame.display.update)
window.show_all()
while gtk.event_pending():
# pygame/SDL event processing goes here
gtk.main_iteration(False)
I tried doing this myself a while ago, and I never got it to work perfectly. Actually I never got it to work at all under Windows, as it kept crashing the entire OS and I ran out of patience. I continued to use it though as it was only important it ran on Linux, and was only a small project. I'd strongly recommend you investigate alternatives. It always felt like a nasty hack, and made me feel dirty.
The Sugar project has several Activities built with PyGTK and PyGame.
They wrote a support lib to achieve this, called Sugargame. You should be able to modify it for regular PyGTK apps instead of Sugar.
Here's a chapter in Sugar's development book about how to use it.
The lib allows for communicating events between GTK and PyGame.
Enjoy!