Continue with wx->gtk portage.

Callbacks for mouse events are "almost" working
in this commit.
This commit is contained in:
Sylvain Tricot 2021-01-25 13:59:03 +01:00
parent b8876a7632
commit f70b1af2f3
1 changed files with 114 additions and 30 deletions

View File

@ -1,8 +1,8 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
# vim: set fdm=indent ts=2 sw=2 sts=2 et tw=80 cc=+1 mouse=a nu : #
# import wx # import wx
import numpy as np import numpy as np
from threading import Timer
# from time import clock # from time import clock
# import copy # import copy
@ -11,7 +11,8 @@ import wx.lib.wxcairo
import gi import gi
gi.require_version("Gtk", "3.0") gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gio, Gtk, Gdk from gi.repository import GLib, Gio, Gtk, Gdk, GObject
GObject.threads_init()
# import ase # import ase
from ase.data import covalent_radii from ase.data import covalent_radii
@ -101,16 +102,22 @@ class ClusterViewer(Gtk.Window):
#self.Bind(wx.EVT_TIMER, self.__evt_timer_cb, self.timer) #self.Bind(wx.EVT_TIMER, self.__evt_timer_cb, self.timer)
self.drawing_area.add_events(Gdk.EventMask.SCROLL_MASK | self.drawing_area.add_events(Gdk.EventMask.SCROLL_MASK |
Gdk.EventMask.POINTER_MOTION_MASK | #Gdk.EventMask.POINTER_MOTION_MASK |
Gdk.EventMask.BUTTON1_MOTION_MASK) Gdk.EventMask.BUTTON_PRESS_MASK |
Gdk.EventMask.BUTTON_RELEASE_MASK |
Gdk.EventMask.BUTTON1_MOTION_MASK |
Gdk.EventMask.BUTTON3_MOTION_MASK)
self.connect("size-allocate", self.__evt_size_cb) self.connect("size-allocate", self.__evt_size_cb)
self.drawing_area.connect("draw", self.__evt_draw_cb) self.drawing_area.connect("draw", self.__evt_draw_cb)
self.drawing_area.connect("scroll-event", self.__evt_mousewheel_cb) self.drawing_area.connect("scroll-event", self.__evt_mousewheel_cb)
self.drawing_area.connect("motion-notify-event", self.__evt_motion_cb) self.drawing_area.connect("motion-notify-event", self.__evt_motion_cb)
#self.drawing_area.connect("button-press-event", self.__evt_press_cb) self.drawing_area.connect("button-press-event", self.__evt_press_cb)
self.drawing_area.connect("button-release-event", self.__evt_release_cb)
#self.drawing_area.connect("button-release-event", self.__evt_release_cb) #self.drawing_area.connect("button-release-event", self.__evt_release_cb)
self.timer_id = None
def show_emitter(self, show=True, alpha=0.25): def show_emitter(self, show=True, alpha=0.25):
_opts = self.sprites_opts.copy() _opts = self.sprites_opts.copy()
if show: if show:
@ -123,11 +130,11 @@ class ClusterViewer(Gtk.Window):
""" """
Attach an Atoms object to the view. Attach an Atoms object to the view.
This will translate the model to the center of mass, move the model This will translate the model to the center of mass, move the model
center to the center of screen and adjust the scale to the largest center to the center of screen and adjust the scale to the largest
dimension of the model dimension of the model
:param rescale: if True, the zoom is computed to view the atoms; if :param rescale: if True, the zoom is computed to view the atoms; if
False, a fixed zoom value is used False, a fixed zoom value is used
""" """
if atoms is None: if atoms is None:
@ -149,7 +156,7 @@ class ClusterViewer(Gtk.Window):
self.atoms_center_of_mass = atoms.get_center_of_mass() self.atoms_center_of_mass = atoms.get_center_of_mass()
# get the largest dimension # get the largest dimension
p = atoms.get_positions() p = atoms.get_positions()
self.atoms_largest_dimension = np.max(np.amax(p, axis=0) - self.atoms_largest_dimension = np.max(np.amax(p, axis=0) -
np.amin(p, axis=0)) np.amin(p, axis=0))
if self.atoms_largest_dimension == 0: if self.atoms_largest_dimension == 0:
self.atoms_largest_dimension = 1.0 self.atoms_largest_dimension = 1.0
@ -203,8 +210,8 @@ class ClusterViewer(Gtk.Window):
self.translation_matrix[-1, (0, 1)] = (x, y) self.translation_matrix[-1, (0, 1)] = (x, y)
self.update_projection_matrix() self.update_projection_matrix()
def select_atoms(self, x, y, w=None, h=None, append=False, def select_atoms(self, x, y, w=None, h=None, append=False, toggle=False):
toggle=False): print("Append=", append, "Toggle=", toggle)
selection = np.array([]) selection = np.array([])
if w is None and h is None: if w is None and h is None:
# get the projections # get the projections
@ -237,10 +244,10 @@ class ClusterViewer(Gtk.Window):
selection = p[:, -1].astype(int) selection = p[:, -1].astype(int)
if toggle: if toggle:
#print(self.selection) print(self.selection)
# whether atoms in the current selection were previously selected # whether atoms in the current selection were previously selected
i = np.in1d(self.selection, selection) i = np.in1d(self.selection, selection)
#print(i) print(i)
self.selection = self.selection[np.invert(i)] self.selection = self.selection[np.invert(i)]
if append: if append:
@ -261,8 +268,9 @@ class ClusterViewer(Gtk.Window):
self.update_drawing() self.update_drawing()
def __evt_size_cb(self, widget, data): def __evt_size_cb(self, widget, data):
#self.timer.Stop() #self.timer.cancel()
#self.timer.Start(self.refresh_delay) #self.timer.start()
self._postpone_drawing()
size = self.get_size() size = self.get_size()
self.back_buffer = cairo.ImageSurface(cairo.FORMAT_RGB24, *size) self.back_buffer = cairo.ImageSurface(cairo.FORMAT_RGB24, *size)
self.create_background_sprite(*size) self.create_background_sprite(*size)
@ -283,6 +291,53 @@ class ClusterViewer(Gtk.Window):
if event.AltDown(): if event.AltDown():
self.mode |= self.MODE_SELECTION_TOGGLE self.mode |= self.MODE_SELECTION_TOGGLE
def __evt_press_cb(self, widget, event):
if event.button == 1:
print("press_cb", event.state)
self.mx = event.x
self.my = event.y
self.capture_screen()
if event.state & Gdk.ModifierType.CONTROL_MASK:
self.mode |= self.MODE_SELECTION
if event.state & Gdk.ModifierType.SHIFT_MASK:
self.mode |= self.MODE_SELECTION_APPEND
if event.state & Gdk.ModifierType.MOD1_MASK:
self.mode |= self.MODE_SELECTION_TOGGLE
def __evt_release_cb(self, widget, event):
if event.button not in (1,3):
return
if self.mode & self.MODE_SELECTION:
self.mode ^= self.MODE_SELECTION
# search for atoms in the selection box
x, y = event.x, event.y
w = h = None
if self.mode & self.MODE_SELECTION_BOX:
self.mode ^= self.MODE_SELECTION_BOX
x, y, w, h = self.selection_box
append = False
if self.mode & self.MODE_SELECTION_APPEND:
self.mode ^= self.MODE_SELECTION_APPEND
append = True
toggle = False
if self.mode & self.MODE_SELECTION_TOGGLE:
self.mode ^= self.MODE_SELECTION_TOGGLE
toggle = True
self.select_atoms(x, y, w, h, append=append, toggle=toggle)
if self.mode == self.MODE_TRANSLATION:
self.mode ^= self.MODE_TRANSLATION
if self.mode & self.MODE_ROTATION:
self.mode ^= self.MODE_ROTATION
self.update_drawing(light=False)
def __evt_left_up_cb(self, event): def __evt_left_up_cb(self, event):
if self.mode & self.MODE_SELECTION: if self.mode & self.MODE_SELECTION:
self.mode ^= self.MODE_SELECTION self.mode ^= self.MODE_SELECTION
@ -351,9 +406,12 @@ class ClusterViewer(Gtk.Window):
self.update_drawing() self.update_drawing()
def __evt_motion_cb(self, widget, event): def __evt_motion_cb(self, widget, event):
#self.timer.Stop() #self.timer.cancel()
#self.timer.Start(self.refresh_delay) #self.timer.start()
if True:#event.state & Gdk.ModifierType.BUTTON1_MASK: self._postpone_drawing()
self._update_flag = True
GLib.timeout_add(self.refresh_delay, self.update_drawing, False)
if event.state & Gdk.ModifierType.BUTTON1_MASK:
print("motion cb...", bool(event.state & print("motion cb...", bool(event.state &
Gdk.ModifierType.BUTTON1_MASK)) Gdk.ModifierType.BUTTON1_MASK))
mx, my = event.x, event.y mx, my = event.x, event.y
@ -374,11 +432,11 @@ class ClusterViewer(Gtk.Window):
self.translate_atoms(self.ox, self.oy) self.translate_atoms(self.ox, self.oy)
self.update_drawing() self.update_drawing()
#elif event.RightIsDown(): #elif event.RightIsDown():
elif event.state & Gdk.ModifierType.BUTTON2_MASK: elif event.state & Gdk.ModifierType.BUTTON3_MASK:
self.mode = self.MODE_ROTATION self.mode = self.MODE_ROTATION
theta = 2. * (float(self.scale0) / self.scale) theta = 2. * (float(self.scale0) / self.scale)
theta = max(1., theta) theta = max(1., theta)
mx, my = event.GetPosition() mx, my = event.x, event.y
dx, dy = (mx - self.mx, my - self.my) dx, dy = (mx - self.mx, my - self.my)
self.mx, self.my = (mx, my) self.mx, self.my = (mx, my)
@ -433,8 +491,9 @@ class ClusterViewer(Gtk.Window):
else: else:
#rot = event.GetWheelRotation() #rot = event.GetWheelRotation()
rot = event.direction rot = event.direction
#self.timer.Stop() #self.timer.cancel()
#self.timer.Start(self.refresh_delay) #self.timer.start()
self._postpone_drawing()
if rot == Gdk.ScrollDirection.UP: if rot == Gdk.ScrollDirection.UP:
factor = self.scale * 1.1 factor = self.scale * 1.1
im_factor = 1 * 1.1 im_factor = 1 * 1.1
@ -445,6 +504,29 @@ class ClusterViewer(Gtk.Window):
self.scale_atoms(factor) self.scale_atoms(factor)
self.update_drawing() self.update_drawing()
def target_fn(self):
print("Timer function!!")
return True
def _postpone_drawing(self):
if self.timer_id:
GLib.source_remove(self.timer_id)
self.timer_id = GLib.timeout_add(self.refresh_delay,
self.update_drawing, False)
def __postpone_drawing(self):
return
try:
self.timer.cancel()
except:
#self.timer = Timer(self.refresh_delay, self.update_drawing,
# kwargs={'light_mode': False})
self.timer = Timer(self.refresh_delay, self.target_fn)
self.timer.start()
print("Timer created")
def capture_screen(self): def capture_screen(self):
# get size of screen # get size of screen
w, h = self.get_size() w, h = self.get_size()
@ -514,7 +596,7 @@ class ClusterViewer(Gtk.Window):
return surface, overlay return surface, overlay
for i, a in enumerate(atom_numbers): for i, a in enumerate(atom_numbers):
surface, overlay = create_surface(a, surface, overlay = create_surface(a,
alpha=self.sprites_opts['alpha'], alpha=self.sprites_opts['alpha'],
glow=self.sprites_opts['glow']) glow=self.sprites_opts['glow'])
atom_surfaces[0, i] = surface atom_surfaces[0, i] = surface
@ -565,7 +647,7 @@ class ClusterViewer(Gtk.Window):
self.atom_surfaces = atom_surfaces self.atom_surfaces = atom_surfaces
try: try:
absorber_number = self.atoms[self.atoms.info['absorber']].number absorber_number = self.atoms[self.atoms.info['absorber']].number
self.absorber_surface = create_surface(absorber_number, alpha=1, self.absorber_surface = create_surface(absorber_number, alpha=1,
glow=True) glow=True)
except: except:
self.atoms.info['absorber'] = -1 self.atoms.info['absorber'] = -1
@ -622,10 +704,10 @@ class ClusterViewer(Gtk.Window):
@classmethod @classmethod
def create_v2p_matrix(cls, left, right, bottom, top, near, far): def create_v2p_matrix(cls, left, right, bottom, top, near, far):
""" """
creates the matrix that transforms coordinates from view space (space creates the matrix that transforms coordinates from view space (space
defined by the bounding box passed as argument) to projection space defined by the bounding box passed as argument) to projection space
this transformation is a scale and offset that maps [left; right], this transformation is a scale and offset that maps [left; right],
[bottom; top], [near; far] to [-1;1], [-1;1], [0;1] [bottom; top], [near; far] to [-1;1], [-1;1], [0;1]
""" """
v2p_matrix = np.eye(4) * -1 v2p_matrix = np.eye(4) * -1
@ -685,12 +767,12 @@ class ClusterViewer(Gtk.Window):
scalebar_bb_width = 200 scalebar_bb_width = 200
scalebar_bb_height = 20 scalebar_bb_height = 20
ctx.set_source_rgba(0., 0., 0., 0.7) ctx.set_source_rgba(0., 0., 0., 0.7)
ctx.rectangle(x + w - scalebar_bb_width - 6, h - scalebar_bb_height - 6, ctx.rectangle(x + w - scalebar_bb_width - 6, h - scalebar_bb_height - 6,
scalebar_bb_width, scalebar_bb_height) scalebar_bb_width, scalebar_bb_height)
ctx.fill() ctx.fill()
ctx.set_source_rgb(1, 1, 1) ctx.set_source_rgb(1, 1, 1)
ctx.rectangle(x + w - scalebar_bb_width, h - scalebar_bb_height, 100, ctx.rectangle(x + w - scalebar_bb_width, h - scalebar_bb_height, 100,
scalebar_bb_height - 12) scalebar_bb_height - 12)
ctx.fill() ctx.fill()
@ -890,7 +972,7 @@ class ClusterViewer(Gtk.Window):
v2 = projections[f[3], :3] - projections[f[0], :3] v2 = projections[f[3], :3] - projections[f[0], :3]
# cross multiply them to get the normal # cross multiply them to get the normal
n = np.cross(v2, v1) n = np.cross(v2, v1)
# If the normal z coordinate is <0, the plane is not visible, so, # If the normal z coordinate is <0, the plane is not visible, so,
# draw it first, otherwise draw it last # draw it first, otherwise draw it last
if n[-1] > 0: if n[-1] > 0:
ind.append(i) ind.append(i)
@ -997,6 +1079,7 @@ class ClusterViewer(Gtk.Window):
allocation = self.drawing_area.get_allocation() allocation = self.drawing_area.get_allocation()
self.drawing_area.queue_draw_area(allocation.x, allocation.y, self.drawing_area.queue_draw_area(allocation.x, allocation.y,
allocation.width, allocation.height) allocation.width, allocation.height)
return False
def swap_buffers(self): def swap_buffers(self):
if self.back_buffer: if self.back_buffer:
@ -1007,7 +1090,7 @@ class ClusterViewer(Gtk.Window):
bitmap = wx.lib.wxcairo.BitmapFromImageSurface(back_buffer) bitmap = wx.lib.wxcairo.BitmapFromImageSurface(back_buffer)
dc = wx.PaintDC(self) dc = wx.PaintDC(self)
dc.DrawBitmap(bitmap, 0, 0) dc.DrawBitmap(bitmap, 0, 0)
def __evt_draw_cb(self, da, ctx): def __evt_draw_cb(self, da, ctx):
if self.back_buffer: if self.back_buffer:
print("Draw called.", ctx) print("Draw called.", ctx)
@ -1022,6 +1105,7 @@ if __name__ == "__main__":
import wx import wx
MgO = bulk('MgO', crystalstructure='rocksalt', a=4.21, cubic=True) MgO = bulk('MgO', crystalstructure='rocksalt', a=4.21, cubic=True)
MgO = MgO.repeat((10,10,10))
#view(MgO) #view(MgO)
#app = wx.App(False) #app = wx.App(False)