[tictactoe] Port to Py3 / GTK3

This commit is contained in:
Yann Leboulanger
2017-09-27 22:20:06 +02:00
parent 999442a9f3
commit 3e7571db91
4 changed files with 116 additions and 106 deletions

View File

@@ -1 +1 @@
from plugin import TictactoePlugin from .plugin import TictactoePlugin

View File

@@ -1,14 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface> <interface>
<requires lib="gtk+" version="2.16"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="window1"> <object class="GtkWindow" id="window1">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkVBox" id="vbox1"> <object class="GtkBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkHBox" id="hbox2"> <object class="GtkBox" id="hbox2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
@@ -16,7 +18,6 @@
<property name="width_request">133</property> <property name="width_request">133</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="xalign">0.029999999329447746</property>
<property name="label" translatable="yes">Board size</property> <property name="label" translatable="yes">Board size</property>
<property name="ellipsize">start</property> <property name="ellipsize">start</property>
<property name="single_line_mode">True</property> <property name="single_line_mode">True</property>

View File

@@ -1,8 +1,8 @@
[info] [info]
name: Tic tac toe name: Tic tac toe
short_name: Tic tac toe short_name: Tic tac toe
version: 0.2.2 version: 1.0
description: Play tic tac toe description: Play tic tac toe
authors = Yann Leboulanger <asterix@lagaule.org> authors = Yann Leboulanger <asterix@lagaule.org>
homepage = http://trac-plugins.gajim.org/wiki/TictactoePlugin homepage = https://dev.gajim.org/gajim/gajim-plugins/wikis/TictactoePlugin
max_gajim_version: 0.15.9 min_gajim_version: 0.16.11

View File

@@ -27,23 +27,26 @@ Tictactoe plugin.
''' '''
from common import helpers from gajim.common import helpers
from common import gajim from gajim.common import app
from plugins import GajimPlugin from gajim.plugins import GajimPlugin
from plugins.plugin import GajimPluginException from gajim.plugins.gajimplugin import GajimPluginException
from plugins.helpers import log_calls, log from gajim.plugins.helpers import log_calls, log
from plugins.gui import GajimPluginConfigDialog from gajim.plugins.gui import GajimPluginConfigDialog
import common.xmpp import nbxmpp
import gtk from gi.repository import Gtk
from gtk import gdk from gi.repository import Gdk
import cairo from gi.repository import GdkPixbuf
import chat_control from gi.repository import cairo
from common import ged import gi
import dialogs gi.require_version('PangoCairo', '1.0')
from common import xmpp from gi.repository import PangoCairo
from common import caps_cache from gajim import chat_control
from common import stanza_session from gajim.common import ged
from common.connection_handlers_events import InformationEvent from gajim import dialogs
from gajim.common import caps_cache
from gajim.common import stanza_session
from gajim.common.connection_handlers_events import InformationEvent
NS_GAMES = 'http://jabber.org/protocol/games' NS_GAMES = 'http://jabber.org/protocol/games'
NS_GAMES_TICTACTOE = NS_GAMES + '/tictactoe' NS_GAMES_TICTACTOE = NS_GAMES + '/tictactoe'
@@ -70,30 +73,30 @@ class TictactoePlugin(GajimPlugin):
@log_calls('TictactoePlugin') @log_calls('TictactoePlugin')
def _compute_caps_hash(self): def _compute_caps_hash(self):
for a in gajim.connections: for a in app.connections:
gajim.caps_hash[a] = caps_cache.compute_caps_hash([ app.caps_hash[a] = caps_cache.compute_caps_hash([
gajim.gajim_identity], gajim.gajim_common_features + \ app.gajim_identity], app.gajim_common_features + \
gajim.gajim_optional_features[a]) app.gajim_optional_features[a])
# re-send presence with new hash # re-send presence with new hash
connected = gajim.connections[a].connected connected = app.connections[a].connected
if connected > 1 and gajim.SHOW_LIST[connected] != 'invisible': if connected > 1 and app.SHOW_LIST[connected] != 'invisible':
gajim.connections[a].change_status(gajim.SHOW_LIST[connected], app.connections[a].change_status(app.SHOW_LIST[connected],
gajim.connections[a].status) app.connections[a].status)
@log_calls('TictactoePlugin') @log_calls('TictactoePlugin')
def activate(self): def activate(self):
if NS_GAMES not in gajim.gajim_common_features: if NS_GAMES not in app.gajim_common_features:
gajim.gajim_common_features.append(NS_GAMES) app.gajim_common_features.append(NS_GAMES)
if NS_GAMES_TICTACTOE not in gajim.gajim_common_features: if NS_GAMES_TICTACTOE not in app.gajim_common_features:
gajim.gajim_common_features.append(NS_GAMES_TICTACTOE) app.gajim_common_features.append(NS_GAMES_TICTACTOE)
self._compute_caps_hash() self._compute_caps_hash()
@log_calls('TictactoePlugin') @log_calls('TictactoePlugin')
def deactivate(self): def deactivate(self):
if NS_GAMES_TICTACTOE in gajim.gajim_common_features: if NS_GAMES_TICTACTOE in app.gajim_common_features:
gajim.gajim_common_features.remove(NS_GAMES_TICTACTOE) app.gajim_common_features.remove(NS_GAMES_TICTACTOE)
if NS_GAMES in gajim.gajim_common_features: if NS_GAMES in app.gajim_common_features:
gajim.gajim_common_features.remove(NS_GAMES) app.gajim_common_features.remove(NS_GAMES)
self._compute_caps_hash() self._compute_caps_hash()
@log_calls('TictactoePlugin') @log_calls('TictactoePlugin')
@@ -102,7 +105,7 @@ class TictactoePlugin(GajimPlugin):
base = Base(self, control) base = Base(self, control)
self.controls.append(base) self.controls.append(base)
# Already existing session? # Already existing session?
conn = gajim.connections[control.account] conn = app.connections[control.account]
sessions = conn.get_sessions(control.contact.jid) sessions = conn.get_sessions(control.contact.jid)
tictactoes = [s for s in sessions if isinstance(s, tictactoes = [s for s in sessions if isinstance(s,
TicTacToeSession)] TicTacToeSession)]
@@ -139,7 +142,7 @@ class TictactoePlugin(GajimPlugin):
session.decline_invitation() session.decline_invitation()
account = obj.conn.name account = obj.conn.name
contact = gajim.contacts.get_first_contact_from_jid(account, obj.jid) contact = app.contacts.get_first_contact_from_jid(account, obj.jid)
if contact: if contact:
name = contact.get_shown_name() name = contact.get_shown_name()
else: else:
@@ -177,23 +180,23 @@ class Base(object):
def create_buttons(self): def create_buttons(self):
# create whiteboard button # create whiteboard button
actions_hbox = self.chat_control.xml.get_object('actions_hbox') actions_hbox = self.chat_control.xml.get_object('actions_hbox')
self.button = gtk.ToggleButton(label=None, use_underline=True) self.button = Gtk.ToggleButton()
self.button.set_property('relief', gtk.RELIEF_NONE) self.button.set_property('relief', Gtk.ReliefStyle.NONE)
self.button.set_property('can-focus', False) self.button.set_property('can-focus', False)
img = gtk.Image() img = Gtk.Image()
img_path = self.plugin.local_file_path('tictactoe.png') img_path = self.plugin.local_file_path('tictactoe.png')
pixbuf = gtk.gdk.pixbuf_new_from_file(img_path) pixbuf = GdkPixbuf.Pixbuf.new_from_file(img_path)
iconset = gtk.IconSet(pixbuf=pixbuf) iconset = Gtk.IconSet(pixbuf=pixbuf)
factory = gtk.IconFactory() factory = Gtk.IconFactory()
factory.add('tictactoe', iconset) factory.add('tictactoe', iconset)
factory.add_default() factory.add_default()
img.set_from_stock('tictactoe', gtk.ICON_SIZE_MENU) img.set_from_stock('tictactoe', Gtk.IconSize.MENU)
self.button.set_image(img) self.button.set_image(img)
send_button = self.chat_control.xml.get_object('send_button') send_button = self.chat_control.xml.get_object('send_button')
send_button_pos = actions_hbox.child_get_property(send_button, send_button_pos = actions_hbox.child_get_property(send_button,
'position') 'position')
actions_hbox.add_with_properties(self.button, 'position', actions_hbox.pack_start(self.button, False, False, 0)
send_button_pos - 1, 'expand', False) actions_hbox.reorder_child(self.button, send_button_pos - 1)
id_ = self.button.connect('toggled', self.on_tictactoe_button_toggled) id_ = self.button.connect('toggled', self.on_tictactoe_button_toggled)
self.chat_control.handlers[id_] = self.button self.chat_control.handlers[id_] = self.button
self.button.show() self.button.show()
@@ -209,7 +212,7 @@ class Base(object):
self.stop_tictactoe('resign') self.stop_tictactoe('resign')
def start_tictactoe(self): def start_tictactoe(self):
self.tictactoe = gajim.connections[self.account].make_new_session( self.tictactoe = app.connections[self.account].make_new_session(
self.fjid, cls=TicTacToeSession) self.fjid, cls=TicTacToeSession)
self.tictactoe.base = self self.tictactoe.base = self
self.tictactoe.begin() self.tictactoe.begin()
@@ -230,11 +233,12 @@ class InvalidMove(Exception):
class TicTacToeSession(stanza_session.StanzaSession): class TicTacToeSession(stanza_session.StanzaSession):
def __init__(self, conn, jid, thread_id, type_): def __init__(self, conn, jid, thread_id, type_):
stanza_session.StanzaSession.__init__(self, conn, jid, thread_id, type_) stanza_session.StanzaSession.__init__(self, conn, jid, thread_id, type_)
contact = gajim.contacts.get_contact(conn.name, contact = app.contacts.get_contact(conn.name,
gajim.get_jid_without_resource(str(jid))) app.get_jid_without_resource(str(jid)))
self.name = contact.get_shown_name() self.name = contact.get_shown_name()
self.base = None self.base = None
self.control = None self.control = None
self.enable_encryption = False
# initiate a session # initiate a session
def begin(self, role_s='x'): def begin(self, role_s='x'):
@@ -256,7 +260,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.received = self.wait_for_invite_response self.received = self.wait_for_invite_response
def send_invitation(self): def send_invitation(self):
msg = xmpp.Message() msg = nbxmpp.Message()
invite = msg.NT.invite invite = msg.NT.invite
invite.setNamespace(NS_GAMES) invite.setNamespace(NS_GAMES)
@@ -265,7 +269,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
game = invite.NT.game game = invite.NT.game
game.setAttr('var', NS_GAMES_TICTACTOE) game.setAttr('var', NS_GAMES_TICTACTOE)
x = xmpp.DataForm(typ='submit') x = nbxmpp.DataForm(typ='submit')
f = x.setField('role') f = x.setField('role')
f.setType('list-single') f.setType('list-single')
f.setValue('x') f.setValue('x')
@@ -288,7 +292,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
game = invite.getTag('game') game = invite.getTag('game')
x = game.getTag('x', namespace='jabber:x:data') x = game.getTag('x', namespace='jabber:x:data')
form = xmpp.DataForm(node=x) form = nbxmpp.DataForm(node=x)
if form.getField('role'): if form.getField('role'):
self.role_o = form.getField('role').getValues()[0] self.role_o = form.getField('role').getValues()[0]
@@ -322,7 +326,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.board = TicTacToeBoard(self, self.rows, self.cols) self.board = TicTacToeBoard(self, self.rows, self.cols)
# accept the invitation, join the game # accept the invitation, join the game
response = xmpp.Message() response = nbxmpp.Message()
join = response.NT.join join = response.NT.join
join.setNamespace(NS_GAMES) join.setNamespace(NS_GAMES)
@@ -350,7 +354,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.their_turn() self.their_turn()
elif msg.getTag('decline', namespace=NS_GAMES): elif msg.getTag('decline', namespace=NS_GAMES):
gajim.nec.push_incoming_event(InformationEvent(None, conn=self.conn, app.nec.push_incoming_event(InformationEvent(None, conn=self.conn,
level='info', pri_txt=_('Invitation refused'), level='info', pri_txt=_('Invitation refused'),
sec_txt=_('%(name)s refused your invitation to play tic tac ' sec_txt=_('%(name)s refused your invitation to play tic tac '
'toe.') % {'name': self.name})) 'toe.') % {'name': self.name}))
@@ -359,7 +363,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.base.button.set_active(False) self.base.button.set_active(False)
def decline_invitation(self): def decline_invitation(self):
msg = xmpp.Message() msg = nbxmpp.Message()
terminate = msg.NT.decline terminate = msg.NT.decline
terminate.setNamespace(NS_GAMES) terminate.setNamespace(NS_GAMES)
@@ -397,12 +401,12 @@ class TicTacToeSession(stanza_session.StanzaSession):
id_ = int(move.getAttr('id')) id_ = int(move.getAttr('id'))
if id_ != self.next_move_id: if id_ != self.next_move_id:
print 'unexpected move id, lost a move somewhere?' log.warn('unexpected move id, lost a move somewhere?')
return return
try: try:
self.board.mark(row, col, self.role_o) self.board.mark(row, col, self.role_o)
except InvalidMove, e: except InvalidMove as e:
# received an invalid move, end the game. # received an invalid move, end the game.
self.board.cheated() self.board.cheated()
self.end_game('cheating') self.end_game('cheating')
@@ -435,8 +439,8 @@ class TicTacToeSession(stanza_session.StanzaSession):
def move(self, row, col): def move(self, row, col):
try: try:
self.board.mark(row, col, self.role_s) self.board.mark(row, col, self.role_s)
except InvalidMove, e: except InvalidMove as e:
print 'you made an invalid move' log.warn('you made an invalid move')
return return
self.send_move(row, col) self.send_move(row, col)
@@ -453,7 +457,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
# sends a move message # sends a move message
def send_move(self, row, column): def send_move(self, row, column):
msg = xmpp.Message() msg = nbxmpp.Message()
msg.setType('chat') msg.setType('chat')
turn = msg.NT.turn turn = msg.NT.turn
@@ -470,7 +474,7 @@ class TicTacToeSession(stanza_session.StanzaSession):
# sends a termination message and ends the game # sends a termination message and ends the game
def end_game(self, reason): def end_game(self, reason):
msg = xmpp.Message() msg = nbxmpp.Message()
terminate = msg.NT.terminate terminate = msg.NT.terminate
terminate.setNamespace(NS_GAMES) terminate.setNamespace(NS_GAMES)
@@ -492,6 +496,14 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.end_game('draw') self.end_game('draw')
self.board.drawn() self.board.drawn()
class DrawBoard(Gtk.DrawingArea):
def __init__(self):
Gtk.DrawingArea.__init__(self)
self.set_size_request(200, 200)
self.set_property('expand', True)
class TicTacToeBoard: class TicTacToeBoard:
def __init__(self, session, rows, cols): def __init__(self, session, rows, cols):
self.session = session self.session = session
@@ -501,7 +513,7 @@ class TicTacToeBoard:
self.rows = rows self.rows = rows
self.cols = cols self.cols = cols
self.board = [ [None] * self.cols for r in xrange(self.rows) ] self.board = [ [None] * self.cols for r in range(self.rows) ]
self.setup_window() self.setup_window()
@@ -520,7 +532,7 @@ class TicTacToeBoard:
r -= 1 r -= 1
c -= 1 c -= 1
for d in xrange(-strike, strike): for d in range(-strike, strike):
r_in_range = 0 <= r+d < self.rows r_in_range = 0 <= r+d < self.rows
c_in_range = 0 <= c+d < self.cols c_in_range = 0 <= c+d < self.cols
@@ -554,24 +566,25 @@ class TicTacToeBoard:
# is the board full? # is the board full?
def full(self): def full(self):
for r in xrange(self.rows): for r in range(self.rows):
for c in xrange(self.cols): for c in range(self.cols):
if self.board[r][c] == None: if self.board[r][c] == None:
return False return False
return True return True
def setup_window(self): def setup_window(self):
self.win = gtk.Window() self.win = Gtk.Window()
draw = DrawBoard()
self.win.add(draw)
self.title_prefix = 'tic-tac-toe with %s' % self.session.name self.title_prefix = 'tic-tac-toe with %s' % self.session.name
self.set_title() self.set_title()
self.win.set_app_paintable(True) self.win.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
self.win.add_events(gdk.BUTTON_PRESS_MASK)
self.win.connect('button-press-event', self.clicked) self.win.connect('button-press-event', self.clicked)
self.win.connect('expose-event', self.expose)
draw.connect('draw', self.do_draw)
self.win.show_all() self.win.show_all()
@@ -591,30 +604,23 @@ class TicTacToeBoard:
self.session.move(row, column) self.session.move(row, column)
# this actually draws the board # this actually draws the board
def expose(self, widget, event): def do_draw(self, widget, cr):
win = widget.window
cr = win.cairo_create()
cr.set_source_rgb(1.0, 1.0, 1.0) cr.set_source_rgb(1.0, 1.0, 1.0)
cr.set_operator(cairo.OPERATOR_SOURCE) layout = PangoCairo.create_layout(cr)
cr.paint() text_height = layout.get_pixel_extents()[1].height
layout = cr.create_layout() (width, height) = self.win.get_size()
text_height = layout.get_pixel_extents()[1][3]
(width, height) = widget.get_size()
row_height = (height - text_height) // self.rows row_height = (height - text_height) // self.rows
col_width = width // self.cols col_width = width // self.cols
cr.set_source_rgb(0, 0, 0) cr.set_source_rgb(0, 0, 0)
cr.set_line_width(2) cr.set_line_width(2)
for x in xrange(1, self.cols): for x in range(1, self.cols):
cr.move_to(col_width * x, 0) cr.move_to(col_width * x, 0)
cr.line_to(col_width * x, height - text_height) cr.line_to(col_width * x, height - text_height)
for x in xrange(1, self.rows): for x in range(1, self.rows):
cr.move_to(0, row_height * x) cr.move_to(0, row_height * x)
cr.line_to(width, row_height * x) cr.line_to(width, row_height * x)
cr.stroke() cr.stroke()
@@ -635,12 +641,13 @@ class TicTacToeBoard:
txt = _('%(name)s cheated') % {'name': self.session.name} txt = _('%(name)s cheated') % {'name': self.session.name}
else: #draw else: #draw
txt = _('It\'s a draw') txt = _('It\'s a draw')
layout.set_text(txt) layout.set_text(txt, -1)
cr.update_layout(layout) # Inform Pango to re-layout the text with the new transformation
cr.show_layout(layout) PangoCairo.update_layout(cr, layout)
PangoCairo.show_layout(cr, layout)
for i in xrange(self.rows): for i in range(self.rows):
for j in xrange(self.cols): for j in range(self.cols):
if self.board[i][j] == 'x': if self.board[i][j] == 'x':
self.draw_x(cr, i, j, row_height, col_width) self.draw_x(cr, i, j, row_height, col_width)
elif self.board[i][j] == 'o': elif self.board[i][j] == 'o':
@@ -648,11 +655,12 @@ class TicTacToeBoard:
def draw_x(self, cr, row, col, row_height, col_width): def draw_x(self, cr, row, col, row_height, col_width):
if self.session.role_s == 'x': if self.session.role_s == 'x':
color = gajim.config.get('outmsgcolor') color = app.config.get('outmsgcolor')
else: else:
color = gajim.config.get('inmsgcolor') color = app.config.get('inmsgcolor')
c = gtk.gdk.Color(color) rgba = Gdk.RGBA()
cr.set_source_color(c) rgba.parse(color)
cr.set_source_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha)
top = row_height * (row + 0.2) top = row_height * (row + 0.2)
bottom = row_height * (row + 0.8) bottom = row_height * (row + 0.8)
@@ -672,11 +680,12 @@ class TicTacToeBoard:
def draw_o(self, cr, row, col, row_height, col_width): def draw_o(self, cr, row, col, row_height, col_width):
if self.session.role_s == 'o': if self.session.role_s == 'o':
color = gajim.config.get('outmsgcolor') color = app.config.get('outmsgcolor')
else: else:
color = gajim.config.get('inmsgcolor') color = app.config.get('inmsgcolor')
c = gtk.gdk.Color(color) rgba = Gdk.RGBA()
cr.set_source_color(c) rgba.parse(color)
cr.set_source_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha)
x = col_width * (col + 0.5) x = col_width * (col + 0.5)
y = row_height * (row + 0.5) y = row_height * (row + 0.5)
@@ -727,13 +736,13 @@ class TictactoePluginConfigDialog(GajimPluginConfigDialog):
def init(self): def init(self):
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path( self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path(
'config_dialog.ui') 'config_dialog.ui')
self.xml = gtk.Builder() self.xml = Gtk.Builder()
self.xml.set_translation_domain('gajim_plugins') self.xml.set_translation_domain('gajim_plugins')
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['vbox1']) self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, ['vbox1'])
self.board_size_spinbutton = self.xml.get_object('board_size') self.board_size_spinbutton = self.xml.get_object('board_size')
self.board_size_spinbutton.get_adjustment().set_all(3, 3, 10, 1, 1, 0) self.board_size_spinbutton.get_adjustment().configure(3, 3, 10, 1, 1, 0)
vbox = self.xml.get_object('vbox1') vbox = self.xml.get_object('vbox1')
self.child.pack_start(vbox) self.get_child().pack_start(vbox, True, True, 0)
self.xml.connect_signals(self) self.xml.connect_signals(self)