reworked the cursor
Build Debian Packages / build (bookworm, debian12) (push) Failing after 8m24s
Build Debian Packages / build (bullseye, debian11) (push) Failing after 5m55s
Build Debian Packages / build (trixie, debian13) (push) Failing after 8m59s

This commit is contained in:
2026-03-26 15:02:59 +01:00
parent 0e1a3eaa92
commit f0ed633f34
2 changed files with 131 additions and 124 deletions
+129 -109
View File
@@ -206,7 +206,7 @@ class DamarisGUI:
else:
self.start_immediately = False
self.statusbar_init( )
#connect event handlers
eventhandlers = {"on_toolbar_run_button_clicked": self.start_experiment,
"on_toolbar_pause_button_toggled": self.pause_experiment,
@@ -241,7 +241,7 @@ class DamarisGUI:
"on_main_notebook_switch_page": self.nopevent,
"main_window_close": self.nopevent
}
self.xml_gui.connect_signals(eventhandlers)
self.xml_gui.connect_signals(eventhandlers)
self.main_window.maximize()
self.main_window.show_all( )
@@ -255,8 +255,8 @@ class DamarisGUI:
self.main_window.connect( "delete-event", self.quit_event )
self.main_window.set_icon_from_file( os.path.join( os.path.dirname( __file__ ), "DAMARIS3.png" ) )
self.main_window.set_title( "DAMARIS-%s" % __version__ )
def statusbar_init( self ):
"""
experiment and result thread status, backend state
@@ -277,7 +277,7 @@ class DamarisGUI:
self.toolbar_run_button.set_sensitive( True )
self.toolbar_stop_button.set_sensitive( False )
self.toolbar_pause_button.set_sensitive( False )
def run( self ):
# prolong lifetime of clipboard till the very end (avoid error message)
self.main_clipboard = self.sw.main_clipboard
@@ -296,7 +296,7 @@ class DamarisGUI:
# event handling: the real acitons in gui programming
# first global events
def nopevent(self, widget, data=None, otherargument=None):
#print(widget)
#print(data)
@@ -310,7 +310,7 @@ class DamarisGUI:
if self.state in [ DamarisGUI.Edit_State, DamarisGUI.Quit_State ]:
self.state = DamarisGUI.Quit_State
# do a cleanup...
if self.sw.experiment_script_textbuffer.get_modified( ):
question = gtk.MessageDialog(parent=self.xml_gui.get_object( "main_window"),
type=gtk.MessageType.WARNING,
@@ -320,7 +320,7 @@ class DamarisGUI:
response = question.run()
question.destroy()
if response == gtk.ResponseType.CANCEL:
return True
elif response == gtk.ResponseType.YES:
@@ -331,7 +331,7 @@ class DamarisGUI:
else:
self.sw.save_file()
self.main_notebook.set_current_page(current_page)
if self.sw.data_handling_textbuffer.get_modified( ):
question = gtk.MessageDialog(parent=self.xml_gui.get_object( "main_window"),
type=gtk.MessageType.WARNING,
@@ -341,7 +341,7 @@ class DamarisGUI:
response = question.run()
question.destroy()
if response == gtk.ResponseType.CANCEL:
return True
elif response == gtk.ResponseType.YES:
@@ -352,8 +352,8 @@ class DamarisGUI:
else:
self.sw.save_file()
self.main_notebook.set_current_page(current_page)
self.config = None
self.sw = None
@@ -474,7 +474,7 @@ class DamarisGUI:
self.experiment_script_statusbar_label.set_text("Waiting for other experiment to finish")
loop_run = 0
loop_run += interval
if self.stop_experiment_flag.isSet():
#Experiment has been stopped, clean up and leave
self.experiment_script_statusbar_label.set_text("Experiment stopped")
@@ -570,18 +570,18 @@ class DamarisGUI:
r = self.si.data.get( "__recentresult", -1 ) + 1
b = self.si.data.get( "__resultsinadvance", -1 ) + 1
e = self.si.data.get( "__recentexperiment", -1 ) + 1
experimentstarttime = self.si.data.get( "__experimentstarted", 0 )
expectedexperimentruntime = self.si.data.get( "__totalexperimentlength", 0 )
experimentsfinishedtime = 0.0
for i in range(0, b):
experimentsfinishedtime += self.si.data.get("__experimentlengths", {}).get(i, 0.0)
experimentruntime = time.time() - experimentstarttime
experimenttimetext = ""
experimenttimetext = ""
if experimentstarttime > 0 and expectedexperimentruntime > 0:
runtimemin, runtimesec = divmod(math.floor(experimentruntime), 60)
runtimehours, runtimemin = divmod(runtimemin, 60)
@@ -589,14 +589,14 @@ class DamarisGUI:
expectedmin, expectedsec = divmod(math.ceil(expectedexperimentruntime), 60)
expectedhours, expectedmin = divmod(expectedmin, 60)
experimenttimetext += "{:02d}:{:02d}:{:02d})".format(int(expectedhours), int(expectedmin), int(expectedsec))
backendtimetext = ""
if experimentsfinishedtime > 0:
finexperimmin, finexperimsec = divmod(round(experimentsfinishedtime), 60)
finexperimhours, finexperimmin = divmod(finexperimmin, 60)
backendtimetext = " ({:02d}:{:02d}:{:02d})".format(int(finexperimhours), int(finexperimmin), int(finexperimsec))
e_text = None
r_text = None
b_text = None
@@ -917,7 +917,7 @@ class DamarisGUI:
# del logtextbuffer, logtext_start, logtext_end, last_end
for l in logtext.splitlines( ):
dump_file.root.log.append( numpy.array( [ l ], dtype="S120" ) )
dump_file.flush( )
dump_file.close( )
self.last_dumped = time.time( )
@@ -1080,13 +1080,13 @@ class LogWindow:
self.textbuffer.insert_at_cursor( date_tag + str( message ) )
self.textview.scroll_to_mark( self.textbuffer.get_insert( ), 0.1 , False, 0.5, 0.5)
gdk.threads_leave( )
def textview_keypress( self, widget, event, data=None ):
if event.state & gdk.ModifierType.CONTROL_MASK != 0:
if event.keyval == gdk.keyval_from_name("f"):
self.damaris_gui.sw.search(None, None)
return True
return False
def __del__( self ):
@@ -1118,17 +1118,17 @@ class ScriptWidgets:
# create and set syntax-highlighting text-buffers as textview backends
self.experiment_script_textbuffer = gtksourceview2.Buffer()
self.data_handling_textbuffer = gtksourceview2.Buffer()
lm = gtksourceview2.LanguageManager()
langpython = lm.get_language("python")
self.experiment_script_textbuffer.set_language(langpython)
self.experiment_script_textbuffer.set_highlight_syntax(True)
self.data_handling_textbuffer.set_language(langpython)
self.data_handling_textbuffer.set_highlight_syntax(True)
fontdesc = pango.FontDescription("monospace")
self.experiment_script_textview.set_buffer( self.experiment_script_textbuffer )
self.experiment_script_textview.set_show_line_numbers(True)
self.experiment_script_textview.set_auto_indent(True)
@@ -1143,32 +1143,32 @@ class ScriptWidgets:
self.data_handling_textview.set_tab_width(4)
self.data_handling_textview.set_smart_home_end(True)
self.data_handling_textview.modify_font(fontdesc)
keywords = """for if else elif in print try finally except global lambda not or pass def
class import from as return yield while continue break assert None True False AccumulatedValue
Accumulation MeasurementResult ADC_Result Experiment synchronize sleep result data isinstance
issubclass min max abs pow range xrange log_range lin_range staggered_range int float zip file
combine_ranges interleaved_range get_sampling_rate uses_statistics write_to_csv write_to_hdf
get_job_id get_description set_description get_xdata get_ydata set_xdate set_ydata"""
keywordsbuffer = gtksourceview2.Buffer()
keywordsbuffer.set_text(keywords)
compexp = self.experiment_script_textview.get_completion()
compres = self.data_handling_textview.get_completion()
compwgen = gtksourceview2.CompletionWords()
compwgen.register(keywordsbuffer)
compwexp = gtksourceview2.CompletionWords()
compwexp.register(self.experiment_script_textbuffer)
compwres = gtksourceview2.CompletionWords()
compwres.register(self.data_handling_textbuffer)
compexp.add_provider(compwgen)
compres.add_provider(compwgen)
compexp.add_provider(compwexp)
compres.add_provider(compwres)
@@ -1185,8 +1185,8 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
self.experiment_script_column_indicator = self.xml_gui.get_object( "experiment_script_column_textfield" )
self.data_handling_line_indicator = self.xml_gui.get_object( "data_handling_line_textfield" )
self.data_handling_column_indicator = self.xml_gui.get_object( "data_handling_column_textfield" )
# some event handlers
self.experiment_script_textbuffer.connect( "modified-changed", self.textviews_modified )
self.experiment_script_textview.connect_after( "move-cursor", self.textviews_moved )
@@ -1208,7 +1208,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
self.toolbar_undo_button = self.xml_gui.get_object( "toolbar_undo_button" )
self.toolbar_redo_button = self.xml_gui.get_object( "toolbar_redo_button" )
self.toolbar_search_button = self.xml_gui.get_object("toolbar_search_button")
# my notebook
self.main_notebook = self.xml_gui.get_object( "main_notebook" )
# config toolbar
@@ -1271,7 +1271,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
def set_toolbuttons_status( self ):
"""
ToDo: care about associated file names
"""
current_page = self.main_notebook.get_current_page( )
@@ -1368,7 +1368,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
textview = None
newline = None
newcol = None
if page == 0:
textview = self.experiment_script_textview
newline = self.experiment_script_line_indicator.get_value_as_int( ) - 1
@@ -1379,7 +1379,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
newcol = self.data_handling_column_indicator.get_value_as_int( ) - 1
else:
print("unknown line/column selector")
textbuffer = textview.get_buffer( )
new_place = textbuffer.get_iter_at_line( newline )
if not newcol > new_place.get_chars_in_line( ):
@@ -1401,13 +1401,13 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
return self.textviews_moved( widget )
else:
return False
def textviews_clicked( self, widget, event ):
return self.textviews_moved( widget )
def textviews_moved( self, widget, text=None, count=None, ext_selection=None, data=None ):
textbuffer = widget.get_buffer( )
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
@@ -1455,43 +1455,43 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
self.search(None, None)
return True
return False
#Handle Backspace (delete more than one space on line beginning)
if event.keyval == 0xFF08:
textbuffer = widget.get_buffer( )
if textbuffer.get_has_selection():
return False
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
linestart_iter = cursor_iter.copy( )
linestart_iter.set_line_offset( 0 )
linebegin = textbuffer.get_text( linestart_iter, cursor_iter, True ).expandtabs(4)
if linebegin.isspace() and len(linebegin) > 0:
linebegin = ' ' * int((len( linebegin ) - 1) / 4) * 4
textbuffer.delete( linestart_iter, cursor_iter )
textbuffer.insert( linestart_iter, linebegin )
return True
elif event.keyval == 0xFF0D:
textbuffer = widget.get_buffer( )
if textbuffer.get_has_selection():
return False
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
lastchar_iter = cursor_iter.copy()
lastchar = lastchar_iter.backward_char( )
if not lastchar_iter.get_char( ) == ":":
return False
linestart_iter = cursor_iter.copy( )
linestart_iter.set_line_offset( 0 )
spaceend_iter = linestart_iter.copy( )
@@ -1499,14 +1499,14 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
spaceend_iter.forward_char( )
linebegin = textbuffer.get_text( linestart_iter, spaceend_iter, True ).expandtabs(4)
indent_length = int((int(len(linebegin)/4)+1)*4)
intext = "\n" + (" " * indent_length)
textbuffer.insert(cursor_iter, intext)
widget.scroll_to_mark( cursor_mark, 0.0, False, 0.5, 0.5 )
return True
#self.textviews_moved(widget)
return False
@@ -1551,7 +1551,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
response = question.run()
question.destroy()
if response == gtk.ResponseType.YES:
self.save_file()
elif response == gtk.ResponseType.CANCEL:
@@ -1657,7 +1657,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
response = question.run()
question.destroy()
if response != gtk.ResponseType.YES:
return True
@@ -1715,7 +1715,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
self.save_file( )
self.main_notebook.set_current_page( current_page )
def undo(self, widget=None, Data=None ):
if not self.editing_state:
return 0
@@ -1725,7 +1725,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
elif current_page == 1:
self.data_handling_textview.do_undo(self.data_handling_textview)
self.set_toolbuttons_status()
def redo(self, widget=None, Data=None ):
if not self.editing_state:
return 0
@@ -1735,20 +1735,20 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
elif current_page == 1:
self.data_handling_textview.do_redo(self.data_handling_textview)
self.set_toolbuttons_status()
def search(self, widget=None, Data=None):
def response(script_widget, response_id, damsw, tb ):
if response_id != -1 and response_id != 1:
script_widget.destroy()
return True
current_page = damsw.main_notebook.get_current_page( )
startiter = None
enditer = None
logview = None
logbuffer = None
if current_page == 0:
if damsw.experiment_script_textbuffer.get_has_selection():
startiter, enditer = damsw.experiment_script_textbuffer.get_selection_bounds()
@@ -1775,14 +1775,14 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
else:
script_widget.destroy()
return True
searchstring = tb.get_text()
if len(searchstring) > 0:
match_start = None
match_end = None
try:
if response_id == -1:
match_start, match_end = startiter.backward_search(searchstring, gtk.TEXT_SEARCH_TEXT_ONLY)
@@ -1790,7 +1790,7 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
match_start, match_end = enditer.forward_search(searchstring, gtk.TEXT_SEARCH_TEXT_ONLY)
except:
pass
if match_start is not None and match_end is not None:
if current_page == 0:
damsw.experiment_script_textbuffer.select_range(match_start, match_end)
@@ -1803,21 +1803,21 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
elif current_page == 3:
logbuffer.select_range(match_start, match_end)
logview.scroll_to_iter(match_start, 0.2)
dialog = gtk.Dialog(title="Search", parent=self.xml_gui.get_object("main_window"), flags=0,
buttons=("_Close", gtk.ResponseType.CLOSE, "Find last", -1, "Find next", 1))
cont = dialog.get_content_area()
markedstring = ""
current_page = self.main_notebook.get_current_page( )
startit = None
endit = None
logbuffer = None
if current_page == 0:
if self.experiment_script_textbuffer.get_has_selection():
startit, endit = self.experiment_script_textbuffer.get_selection_bounds()
@@ -1832,20 +1832,20 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
if logbuffer.get_has_selection():
startit, endit = logbuffer.get_selection_bounds()
markedstring = logbuffer.get_text(startit, endit, True)
markedstring = markedstring.strip()
textbox = gtk.Entry()
textbox.set_text(markedstring)
cont.add(textbox)
dialog.set_default_response(1)
dialog.connect("response", response, self, textbox)
dialog.set_resizable(False)
dialog.show()
if startit is not None and endit is not None:
if current_page == 0:
self.experiment_script_textbuffer.select_range(startit, endit)
@@ -1853,9 +1853,9 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
self.data_handling_textbuffer.select_range(startit, endit)
elif current_page == 3:
logbuffer.select_range(startit, endit)
def new_file( self, widget, Data=None ):
@@ -1872,12 +1872,12 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
response = question.run()
question.destroy()
if response == gtk.ResponseType.YES:
self.save_file()
elif response == gtk.ResponseType.CANCEL:
return 0
self.set_scripts( "", None )
self.exp_script_filename = None
elif current_page == 1:
@@ -1890,12 +1890,12 @@ get_job_id get_description set_description get_xdata get_ydata set_xdate set_yda
response = question.run()
question.destroy()
if response == gtk.ResponseType.YES:
self.save_file()
elif response == gtk.ResponseType.CANCEL:
return 0
self.set_scripts( None, "" )
self.res_script_filename = None
self.set_toolbuttons_status( )
@@ -2014,7 +2014,7 @@ pygobject version %(pygobject)s
info_text %= { "moduleversions": components_text % components_versions, "damarisversion": __version__ }
info_textbuffer.set_text( info_text )
del info_textbuffer, info_text, components_text, components_versions
for self.system_default_filename in self.system_default_filenames:
if self.system_default_filename:
if os.access( self.system_default_filename, os.R_OK ):
@@ -2204,7 +2204,7 @@ pygobject version %(pygobject)s
dirs = os.path.dirname( filename )
if not os.path.isdir( dirs ):
os.makedirs( dirs )
print("save config to: "+filename)
configfile = open( filename, "w" )
@@ -2287,6 +2287,23 @@ class MonitorWidgets:
# add matplotlib toolbar (display_table, 2nd row)
self.matplot_toolbar = matplotlib.backends.backend_gtk3.NavigationToolbar2GTK3( self.matplot_canvas )
# remove subplots:
#self.matplot_toolbar.DeleteToolByPos(1)
#self.matplot_toolbar.remove(self.matplot_toolbar.get_nth_item(3))
self.matplot_toolbar.remove(self.matplot_toolbar.get_nth_item(6))
# now let's add a button to the toolbar
button = gtk.ToggleButton(label='')
try:
image = gtk.Image.new_from_file("/usr/share/icons/breeze/actions/24/crosshairs-symbolic.svg")
button.set_image(image)
except:
pass
button.set_tooltip_text('Enable Crosshairs')
button.show()
button.connect('toggled', self.toggle_cursor)
toolitem = gtk.ToolItem()
toolitem.add(button)
self.matplot_toolbar.insert(toolitem, 5)
# Make cursor position text bigger and bold - access message label directly
try:
@@ -2314,6 +2331,12 @@ class MonitorWidgets:
# enable display scaling
self.display_x_scaling_combobox.set_active( 0 )
self.display_y_scaling_combobox.set_active( 0 )
for i in ("lin", "log10"):
self.display_x_scaling_combobox.append_text(i)
self.display_y_scaling_combobox.append_text(i)
self.display_x_scaling_combobox.append_text("ppm")
self.display_x_scaling_combobox.set_active(0)
self.display_y_scaling_combobox.set_active(0)
self.display_x_scaling_combobox.set_sensitive( True )
self.display_y_scaling_combobox.set_sensitive( True )
@@ -2665,7 +2688,7 @@ class MonitorWidgets:
tmpdata = tempfile.TemporaryFile(mode="w+" )
tmpdata.write("# saved from monitor as %s\n" % data_to_save[ 0 ] )
data_to_save[ 1 ].write_to_csv( tmpdata )
parentfordialog = self.main_window
# show save dialog
@@ -2685,7 +2708,7 @@ class MonitorWidgets:
response = question.run()
question.destroy()
if response != gtk.ResponseType.YES:
return True
@@ -2720,19 +2743,16 @@ class MonitorWidgets:
def toggle_cursor(self, widget):
"""Toggle matplotlib crosshair cursor"""
from matplotlib.widgets import Cursor
if widget.get_active():
if hasattr(self, 'matplot_cursor') and self.matplot_cursor is not None:
self.matplot_cursor.disconnect_events()
self.matplot_cursor = Cursor(self.matplot_axes, useblit=False, color='red', linewidth=1)
self.matplot_cursor = matplotlib.widgets.Cursor(self.matplot_axes, useblit=False, color='red', linewidth=1)
else:
if hasattr(self, 'matplot_cursor') and self.matplot_cursor is not None:
self.matplot_cursor.disconnect_events()
self.matplot_cursor = None
if hasattr(self, 'matplot_cursor'):
self.matplot_cursor = None
self.matplot_canvas.draw_idle()
def on_canvas_button_press(self, widget, event):
"""Handle button press events on matplotlib canvas"""
if event.button == 3: # Right click
self.show_canvas_context_menu(event)
return True
@@ -2752,7 +2772,6 @@ class MonitorWidgets:
toggle_cursor.connect("toggled", self.toggle_cursor)
menu.append(toggle_cursor)
# Copy as PNG
item_copy_png = gtk.MenuItem(label="Copy as PNG")
item_copy_png.connect("activate", self.copy_canvas_as_png)
@@ -2843,9 +2862,10 @@ class MonitorWidgets:
"""
self.display_x_scaling_combobox.set_sensitive( True )
self.display_y_scaling_combobox.set_sensitive( True )
x_scale = self.display_x_scaling_combobox.get_active_text()
y_scale = self.display_y_scaling_combobox.get_active_text()
print(x_scale)
if x_scale == "lin":
self.matplot_axes.set_xscale("linear")
elif x_scale == "log10":
@@ -2948,10 +2968,10 @@ class MonitorWidgets:
self.matplot_axes.set_xlim(xmin, xmax)
ylim_min, ylim_max = self.matplot_axes.get_ylim()
# Check if y-axis is log scale
yscale = self.matplot_axes.get_yscale()
if yscale == 'log' or yscale == 'symlog':
# For log scales, work with log values to avoid issues with
# small numbers (undetected rescale)
+2 -15
View File
@@ -1044,7 +1044,7 @@ Public License instead of this License.
<object class="GtkLabel" id="label65">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Source </property>
<property name="label" translatable="yes">Source:</property>
</object>
<packing>
<property name="expand">False</property>
@@ -1053,7 +1053,7 @@ Public License instead of this License.
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="display_source_combobox">
<object class="GtkComboBox" id="display_source_combobox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="add-tearoffs">True</property>
@@ -1075,12 +1075,6 @@ Public License instead of this License.
<property name="can-focus">False</property>
<property name="focus-on-click">False</property>
<signal name="changed" handler="on_display_y_scaling_combobox_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="renderer1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left-attach">1</property>
@@ -1114,12 +1108,6 @@ Public License instead of this License.
<property name="can-focus">False</property>
<property name="focus-on-click">False</property>
<signal name="changed" handler="on_display_x_scaling_combobox_changed" swapped="no"/>
<child>
<object class="GtkCellRendererText" id="renderer2"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left-attach">1</property>
@@ -1931,7 +1919,6 @@ Public License instead of this License.
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="CompLibs">
<property name="visible">True</property>
<property name="model">model3</property>