1616
1717import ctypes
1818import os
19+ import platform
1920import pyperclip
2021import requests
2122import string
2526from nltk .tokenize import RegexpTokenizer
2627from outdated import check_outdated
2728from PyMultiDictionary import MultiDictionary
28- from typing import Optional , Tuple
29+ from typing import Optional , Tuple , Callable
2930from warnings import warn
3031
3132sys .path .append ('../' )
3637import pydetex .utils as ut
3738import pydetex .version
3839
40+ from pydetex import __url_bug_tracker__
3941from pydetex ._fonts import FONT_TAGS
4042from pydetex ._gui_settings import Settings
4143
@@ -138,7 +140,7 @@ def __init__(self) -> None:
138140 self ._tab_spaces = 4
139141
140142 # In text
141- f1 = tk .Frame (self ._root , border = 0 , width = window_size [0 ], height = window_size [2 ])
143+ f1 = tk .Frame (self ._root , width = window_size [0 ], height = window_size [2 ])
142144 f1 .pack (fill = 'both' , padx = 10 )
143145 f1 .pack_propagate (False )
144146
@@ -157,7 +159,7 @@ def __init__(self) -> None:
157159 self ._text_in .tab_spaces = self ._tab_spaces
158160
159161 # Out text
160- f2 = tk .Frame (self ._root , border = 0 , width = window_size [0 ], height = window_size [2 ])
162+ f2 = tk .Frame (self ._root , width = window_size [0 ], height = window_size [2 ])
161163 f2 .pack (fill = 'both' , padx = 10 , pady = (window_size [3 ], 0 ))
162164 f2 .pack_propagate (False )
163165
@@ -199,7 +201,7 @@ def __init__(self) -> None:
199201 # ----------------------------------------------------------------------
200202 # Status bar
201203 # ----------------------------------------------------------------------
202- ttk .Separator (self ._root , orient = 'horizontal' ).pack (side = 'top' , fill = 'x' )
204+ ttk .Separator (self ._root ).pack (side = 'top' , fill = 'x' )
203205 status_fg = '#999999' if ut .IS_OSX else '#666666'
204206
205207 f4 = tk .Frame (self ._root , width = window_size [0 ], height = 26 )
@@ -208,27 +210,32 @@ def __init__(self) -> None:
208210
209211 # Detected language
210212 show_status = 0.2 if window_size [0 ] > 750 else 0
211- self ._status_bar_lang = gui_ut .make_label (f4 , w = window_size [0 ] * (0.4 + (0.2 - show_status )), h = 20 ,
212- side = tk .LEFT , fg = status_fg , bd = 0 , relief = tk .SUNKEN , anchor = tk .W ,
213- pad = (0 , 0 , 0 , 10 ), separator = True )
213+ self ._status_bar_lang = gui_ut .make_label (
214+ f4 , w = window_size [0 ] * (0.4 + (0.2 - show_status )), h = 20 ,
215+ side = tk .LEFT , fg = status_fg , bd = 0 , relief = tk .SUNKEN , anchor = tk .W ,
216+ pad = (0 , 0 , 0 , 10 ), separator = True )
214217
215218 # Status
216- self ._status_bar_status = gui_ut .make_label (f4 , w = window_size [0 ] * show_status , h = 20 , side = tk .LEFT ,
217- fg = status_fg , bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ),
218- separator = True )
219+ self ._status_bar_status = gui_ut .make_label (
220+ f4 , w = window_size [0 ] * show_status , h = 20 , side = tk .LEFT ,
221+ fg = status_fg , bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ),
222+ separator = True )
219223
220224 # Cursor
221- self ._status_bar_cursor = gui_ut .make_label (f4 , w = window_size [0 ] * 0.11 , h = 20 , side = tk .LEFT , fg = status_fg ,
222- bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ))
225+ self ._status_bar_cursor = gui_ut .make_label (
226+ f4 , w = window_size [0 ] * 0.11 , h = 20 , side = tk .LEFT , fg = status_fg ,
227+ bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ))
223228
224229 # Cursor selected
225- self ._status_bar_cursor_sel = gui_ut .make_label (f4 , w = window_size [0 ] * 0.14 , h = 20 , side = tk .LEFT , fg = status_fg ,
226- bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ),
227- separator = True )
230+ self ._status_bar_cursor_sel = gui_ut .make_label (
231+ f4 , w = window_size [0 ] * 0.14 , h = 20 , side = tk .LEFT , fg = status_fg ,
232+ bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ),
233+ separator = True )
228234
229235 # Total processed words
230- self ._status_bar_words = gui_ut .make_label (f4 , w = window_size [0 ] * 0.15 , h = 20 , side = tk .LEFT , fg = status_fg ,
231- bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ))
236+ self ._status_bar_words = gui_ut .make_label (
237+ f4 , w = window_size [0 ] * 0.15 , h = 20 , side = tk .LEFT , fg = status_fg ,
238+ bd = 0 , relief = tk .SUNKEN , anchor = tk .W , pad = (0 , 0 , 0 , 5 ))
232239
233240 # ----------------------------------------------------------------------
234241 # Final settings
@@ -240,10 +247,18 @@ def __init__(self) -> None:
240247 self ._clip = True
241248 try :
242249 pyperclip .paste ()
243- except pyperclip . PyperclipException :
250+ except Exception as e :
244251 self ._clip = False
245- error = 'pyperclip is not available on your system (copy/paste mechanism). GUI buttons were disabled'
246- warn (error )
252+ pyperclip_error : str = 'pyperclip is not available on your system (copy/paste mechanism). '
253+ if os .getenv ('XDG_SESSION_TYPE' ) == 'wayland' :
254+ pyperclip_error += 'Please install package "wl-clipboard"'
255+ elif platform .system () == 'Linux' :
256+ pyperclip_error += 'On Linux, install "xclip" or "xsel"'
257+ else :
258+ pyperclip_error += (f'An unrecognized error happened ({ e } ). '
259+ f'Please create a new issue on { __url_bug_tracker__ } ' )
260+ pyperclip_error += 'GUI buttons have been disabled'
261+ warn (pyperclip_error )
247262 self ._process_clip_button ['state' ] = tk .DISABLED
248263 self ._copy_clip_button ['state' ] = tk .DISABLED
249264
@@ -263,6 +278,21 @@ def __init__(self) -> None:
263278 self ._text_out ['state' ] = tk .DISABLED
264279 self ._root .update ()
265280
281+ def __after (self , ms : int , func : Callable ) -> str :
282+ """
283+ Executes function after some milliseconds.
284+
285+ :param ms: Time delay
286+ :param func: Function to be executed
287+ :return: Call ID
288+ """
289+ try :
290+ # noinspection PyTypeChecker
291+ return self ._root .after (ms , func )
292+ except tk .TclError :
293+ pass
294+ return ''
295+
266296 def _open_file (self ) -> None :
267297 """
268298 Opens a file.
@@ -318,7 +348,7 @@ def _status(self, text: str, clear: bool = False, clear_time: int = 500) -> None
318348 self ._root .after_cancel (self ._status_clear_event_id )
319349 self ._status_bar_status ['text' ] = text
320350 if clear :
321- self ._status_clear_event_id = self ._root . after (clear_time , self ._status_clear )
351+ self ._status_clear_event_id = self .__after (clear_time , self ._status_clear )
322352
323353 def _status_clear (self ) -> None :
324354 """
@@ -339,7 +369,7 @@ def _process_in_key(self, event: 'tk.Event') -> Optional['tk.Event']:
339369
340370 if event .char in string .printable and event .char != '' :
341371 self ._status (self ._cfg .lang ('status_writing' ), True , 1000 )
342- self ._detect_language_event_id = self ._root . after (100 , self ._detect_language )
372+ self ._detect_language_event_id = self .__after (100 , self ._detect_language )
343373 self ._process_cursor_in (event )
344374 return event
345375
@@ -350,7 +380,7 @@ def _process_cursor_in(self, event: Optional['tk.Event']) -> Optional['tk.Event'
350380 :param event: Event
351381 :return: Event
352382 """
353- self ._root . after (50 , self ._process_cursor_event )
383+ self .__after (50 , self ._process_cursor_event )
354384 return event
355385
356386 def _process_focusin_in (self , event : Optional ['tk.Event' ]) -> Optional ['tk.Event' ]:
@@ -460,8 +490,8 @@ def start(self) -> None:
460490 """
461491 # noinspection PyProtectedMember
462492 if self ._cfg ._last_opened_day_diff >= 7 :
463- self ._root . after (1000 , self ._check_version_event )
464- self ._root . after (100 , lambda : self ._root .lift ())
493+ self .__after (1000 , self ._check_version_event )
494+ self .__after (100 , lambda : self ._root .lift ())
465495 self ._text_in .redraw ()
466496 self ._text_out .redraw ()
467497 self ._root .mainloop ()
@@ -510,7 +540,7 @@ def _process(self) -> None:
510540 self ._root ['cursor' ] = 'wait'
511541 except tk .TclError :
512542 pass
513- self ._root . after (1 , lambda : self ._process_inner ())
543+ self .__after (1 , lambda : self ._process_inner ())
514544
515545 def _process_inner (self ) -> None :
516546 """
0 commit comments