# Dindo Bot # Copyright (c) 2018 - 2019 AXeL import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk, GdkPixbuf, GObject from lib import tools, logger, data, parser, settings, accounts, maps from threads.bot import BotThread from lib.shared import LogType, DebugLevel, __program_name__, GameVersion from .dev import DevToolsWidget from .custom import * from .dialog import * from threading import Thread class BotWindow(Gtk.ApplicationWindow): def __init__(self, title=__program_name__): GObject.threads_init() # allow threads to update GUI Gtk.Window.__init__(self, title=title) logger.add_separator() # Initialise class attributes self.game_window = None self.game_window_location = None self.bot_path = None self.bot_thread = None self.args = tools.get_cmd_args() # Get settings self.settings = settings.load() # Header Bar self.create_header_bar(title) # Tabs self.create_tabs() # Window self.set_icon_from_file(tools.get_full_path('icons/logo.png')) #self.set_size_request(350, 700) self.set_default_size(350, 700) self.set_resizable(False) self.connect('key-press-event', self.on_key_press) #self.connect('configure-event', self.on_resize_or_move) #self.connect('window-state-event', self.on_minimize) self.connect('destroy', Gtk.main_quit) self.show_all() self.unbind_button.hide() if not self.settings['Debug']['Enabled']: self.debug_page.hide() if not self.settings['State']['EnablePodBar']: self.podbar_box.hide() if not self.settings['State']['EnableMiniMap']: self.minimap_box.hide() def on_key_press(self, widget, event): if self.settings['EnableShortcuts']: # get keyname keyname = Gdk.keyval_name(event.keyval) ctrl = (event.state & Gdk.ModifierType.CONTROL_MASK) alt = (event.state & Gdk.ModifierType.MOD1_MASK) shift = (event.state & Gdk.ModifierType.SHIFT_MASK) # handle shortcuts for action in self.settings['Shortcuts']: value = self.settings['Shortcuts'][action] if value is not None: keys = value.split('+') if (len(keys) == 1 and keys[0] == keyname) or (len(keys) == 2 and ((keys[0] == 'Ctrl' and ctrl) or (keys[0] == 'Alt' and alt) or (keys[0] == 'Shift' and shift)) and keys[1] == keyname): # run actions if action == 'Start': self.start_button.emit('clicked') elif action == 'Pause': self.pause_button.emit('clicked') elif action == 'Stop': self.stop_button.emit('clicked') elif action == 'Minimize': self.iconify() elif action == 'Take Game Screenshot': self.take_screenshot_button.emit('clicked') elif action == 'Focus Game': self.focus_game() # stop event propagation return True # focus game if self.bot_thread and self.bot_thread.isAlive(): self.focus_game() def on_minimize(self, widget, event): if event.window.get_state() == Gdk.WindowState.ICONIFIED: self.pause_bot() def on_resize_or_move(self, widget, event): self.pause_bot() def pop(self, text_buffer, max=100): start_iter = text_buffer.get_start_iter() end_iter = text_buffer.get_end_iter() lines = text_buffer.get_text(start_iter, end_iter, True).splitlines() if len(lines) >= max: new_text = '\n'.join(lines[1:]) + '\n' # [1:] to remove the first line text_buffer.set_text(new_text) def log(self, text, type=LogType.Normal): # pop first line if we reached the max number of lines self.pop(self.log_buf) # append to text view position = self.log_buf.get_end_iter() new_text = '[' + tools.get_time() + '] ' + text + '\n' if type == LogType.Success: self.log_buf.insert_with_tags(position, new_text, self.green_text_tag) elif type == LogType.Error: self.log_buf.insert_with_tags(position, new_text, self.red_text_tag) elif type == LogType.Info: self.log_buf.insert_with_tags(position, new_text, self.blue_text_tag) else: self.log_buf.insert(position, new_text) # call logger if type == LogType.Error: logger.error(text) else: logger.new_entry(text) def debug(self, text, level=DebugLevel.Normal): # append to text view if self.settings['Debug']['Enabled'] and level >= self.settings['Debug']['Level']: self.pop(self.debug_buf) position = self.debug_buf.get_end_iter() self.debug_buf.insert(position, '[' + tools.get_time() + '] ' + text + '\n') logger.debug(text) def on_about_button_clicked(self, button): dialog = AboutDialog(transient_for=self) dialog.run() def on_preferences_button_clicked(self, button): dialog = PreferencesDialog(transient_for=self) dialog.run() def on_accounts_button_clicked(self, button): dialog = AccountsDialog(transient_for=self) dialog.run() def on_take_screenshot_button_clicked(self, button): if self.game_window and not self.game_window.is_destroyed(): screenshot_name = 'screenshot_' + tools.get_date_time() screenshot_path = tools.get_full_path(screenshot_name) tools.take_window_screenshot(self.game_window, screenshot_path) self.log("Screenshot saved to '%s'" % screenshot_path, LogType.Info) def create_header_bar(self, title): ### Header Bar hb = Gtk.HeaderBar(title=title) logo = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/logo.png'), 24, 24) hb.pack_start(Gtk.Image(pixbuf=logo)) hb.set_show_close_button(True) self.set_titlebar(hb) ## Settings button self.settings_button = Gtk.Button() self.settings_button.set_image(Gtk.Image(icon_name='open-menu-symbolic')) self.settings_button.connect('clicked', lambda button: self.popover.show_all()) hb.pack_end(self.settings_button) self.popover = Gtk.Popover(relative_to=self.settings_button, position=Gtk.PositionType.BOTTOM) self.popover.set_border_width(2) box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) self.popover.add(box) # Preferences button preferences_button = Gtk.ModelButton(' Preferences') preferences_button.set_alignment(0, 0.5) preferences_button.set_image(Gtk.Image(icon_name='preferences-desktop')) preferences_button.connect('clicked', self.on_preferences_button_clicked) box.add(preferences_button) # Accounts button accounts_button = Gtk.ModelButton(' Accounts') accounts_button.set_alignment(0, 0.5) accounts_button.set_image(Gtk.Image(icon_name='dialog-password')) accounts_button.connect('clicked', self.on_accounts_button_clicked) box.add(accounts_button) # Take Game Screenshot button self.take_screenshot_button = Gtk.ModelButton(' Take Game Screenshot') self.take_screenshot_button.set_alignment(0, 0.5) self.take_screenshot_button.set_image(Gtk.Image(icon_name='camera-photo')) self.take_screenshot_button.set_sensitive(False) self.take_screenshot_button.connect('clicked', self.on_take_screenshot_button_clicked) box.add(self.take_screenshot_button) # Open Log File button open_log_button = Gtk.ModelButton(' Open Log File') open_log_button.set_alignment(0, 0.5) open_log_button.set_image(Gtk.Image(icon_name='text-x-generic')) open_log_button.connect('clicked', lambda button: tools.open_file_in_editor(logger.get_filename())) box.add(open_log_button) # About button about_button = Gtk.ModelButton(' About') about_button.set_alignment(0, 0.5) about_button.set_image(Gtk.Image(icon_name='help-about')) about_button.connect('clicked', self.on_about_button_clicked) box.add(about_button) def log_view_auto_scroll(self, textview, event): adj = textview.get_vadjustment() adj.set_value(adj.get_upper() - adj.get_page_size()) def debug_view_auto_scroll(self, textview, event): adj = textview.get_vadjustment() adj.set_value(adj.get_upper() - adj.get_page_size()) def create_tabs(self): bot_notebook = Gtk.Notebook() bot_notebook.set_border_width(2) self.add(bot_notebook) self.config_notebook = Gtk.Notebook() self.log_notebook = Gtk.Notebook() self.log_notebook.set_size_request(-1, 200) ## Log Tab log_page = Gtk.ScrolledWindow() self.log_view = Gtk.TextView() self.log_view.set_border_width(5) self.log_view.set_editable(False) self.log_view.set_wrap_mode(Gtk.WrapMode.WORD) self.log_view.connect('size-allocate', self.log_view_auto_scroll) self.log_buf = self.log_view.get_buffer() self.red_text_tag = self.log_buf.create_tag('red', foreground='#dc3545') self.green_text_tag = self.log_buf.create_tag('green', foreground='#28a745') self.blue_text_tag = self.log_buf.create_tag('blue', foreground='#007bff') log_page.add(self.log_view) self.log_notebook.append_page(log_page, Gtk.Label('Log')) ## Debug Tab self.debug_page = Gtk.ScrolledWindow() self.debug_view = Gtk.TextView() self.debug_view.set_border_width(5) self.debug_view.set_editable(False) self.debug_view.set_wrap_mode(Gtk.WrapMode.WORD) self.debug_view.modify_bg(Gtk.StateType.NORMAL, Gdk.color_parse('black')) self.debug_view.modify_fg(Gtk.StateType.NORMAL, Gdk.color_parse('white')) self.debug_view.connect('size-allocate', self.debug_view_auto_scroll) self.debug_buf = self.debug_view.get_buffer() self.debug_page.add(self.debug_view) self.log_notebook.append_page(self.debug_page, Gtk.Label('Debug')) ### Bot Tab bot_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) bot_page.set_border_width(2) bot_page.add(self.config_notebook) bot_page.pack_start(self.log_notebook, True, True, 0) bot_notebook.append_page(bot_page, Gtk.Label('Bot')) ## Config Tab self.bot_config_widgets = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) self.bot_config_widgets.set_border_width(10) self.config_notebook.append_page(self.bot_config_widgets, Gtk.Label('Config')) ## Game Window self.bot_config_widgets.add(Gtk.Label('<b>Game Window</b>', xalign=0, use_markup=True)) game_window_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) self.bot_config_widgets.add(game_window_box) # ComboBox self.game_window_combo = Gtk.ComboBoxText() self.game_window_combo.set_margin_left(10) self.populate_game_window_combo() self.game_window_combo.connect('changed', self.on_game_window_combo_changed) game_window_box.pack_start(self.game_window_combo, True, True, 0) # Refresh self.refresh_button = Gtk.Button() self.refresh_button.set_image(Gtk.Image(icon_name='view-refresh')) self.refresh_button.set_tooltip_text('Refresh') self.refresh_button.connect('clicked', self.on_refresh_button_clicked) game_window_box.add(self.refresh_button) # Unbind self.unbind_button = Gtk.Button() self.unbind_button.set_image(Gtk.Image(icon_name='view-restore')) self.unbind_button.set_tooltip_text('Unbind') self.unbind_button.connect('clicked', self.on_unbind_button_clicked) game_window_box.add(self.unbind_button) ## Bot Path self.bot_config_widgets.add(Gtk.Label('<b>Bot Path</b>', xalign=0, use_markup=True)) bot_path_filechooserbutton = FileChooserButton(title='Choose bot path', filter=('Bot Path', '*.path')) bot_path_filechooserbutton.set_margin_left(10) bot_path_filechooserbutton.set_current_folder(tools.get_full_path('paths')) bot_path_filechooserbutton.connect('file-set', self.on_bot_path_changed) self.bot_config_widgets.add(bot_path_filechooserbutton) ## Start From Step hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.set_margin_left(10) self.bot_config_widgets.add(hbox) hbox.add(Gtk.Label('Start From Step')) self.step_spin_button = SpinButton(min=1, max=1000) self.step_spin_button.set_margin_left(10) hbox.pack_end(self.step_spin_button, False, False, 0) ## Repeat Path hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.set_margin_left(10) hbox.add(Gtk.Label('Repeat Path')) self.bot_config_widgets.add(hbox) # Switch self.repeat_switch = Gtk.Switch() self.repeat_switch.connect('notify::active', lambda switch, pspec: self.repeat_spin_button.set_sensitive(switch.get_active())) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) vbox.pack_start(self.repeat_switch, True, False, 0) hbox.add(vbox) # SpinButton self.repeat_spin_button = SpinButton(min=2, max=1000) self.repeat_spin_button.set_tooltip_text('Number of times') self.repeat_spin_button.set_sensitive(False) hbox.pack_end(self.repeat_spin_button, False, False, 0) ## Connect To Account hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('<b>Connect To Account</b>', xalign=0, use_markup=True)) self.bot_config_widgets.add(hbox) # Switch self.connect_to_account_switch = Gtk.Switch() self.connect_to_account_switch.connect('notify::active', lambda switch, pspec: self.connect_to_account_box.set_sensitive(switch.get_active())) hbox.pack_end(self.connect_to_account_switch, False, False, 0) # Box self.connect_to_account_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) self.connect_to_account_box.set_margin_left(10) self.connect_to_account_box.set_sensitive(False) self.bot_config_widgets.add(self.connect_to_account_box) # Account hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('Account')) self.connect_to_account_box.add(hbox) # Combo accounts_list = accounts.load() self.accounts_combo = TextValueComboBox(accounts_list, model=Gtk.ListStore(str, int), text_key='login', value_key='id', sort_key='position') self.accounts_combo.set_size_request(120, -1) hbox.pack_end(self.accounts_combo, False, False, 0) # Disconnect after hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('Disconnect after')) self.connect_to_account_box.add(hbox) # Switch self.disconnect_after_switch = Gtk.Switch() hbox.pack_end(self.disconnect_after_switch, False, False, 0) ## State Tab bot_state_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) bot_state_page.set_border_width(10) self.config_notebook.append_page(bot_state_page, Gtk.Label('State')) # Pod self.podbar_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) bot_state_page.add(self.podbar_box) self.podbar_box.add(Gtk.Label('<b>Pod</b>', xalign=0, use_markup=True)) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.podbar_box.pack_start(vbox, True, True, 0) self.podbar = Gtk.ProgressBar() vbox.pack_start(self.podbar, True, False, 0) # MiniMap self.minimap_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) bot_state_page.add(self.minimap_box) self.minimap_box.add(Gtk.Label('<b>MiniMap</b>', xalign=0, use_markup=True)) self.minimap = MiniMap(grid_size=(14, 14)) self.minimap.set_size_request(-1, 240) self.minimap_box.add(self.minimap) ## Dev tools Tab if '--dev' in self.args: dev_tools_page = DevToolsWidget(self) self.config_notebook.append_page(dev_tools_page, Gtk.Label('Dev Tools')) #self.config_notebook.show_all() #self.config_notebook.set_current_page(2) ## Start button_box = ButtonBox(centered=True, linked=True) bot_page.pack_end(button_box, False, False, 0) self.start_button = Gtk.Button() self.start_button.set_tooltip_text('Start') self.start_button.set_image(Gtk.Image(icon_name='media-playback-start')) self.start_button.connect('clicked', self.on_start_button_clicked) button_box.add(self.start_button) ## Pause self.pause_button = Gtk.Button() self.pause_button.set_image(Gtk.Image(icon_name='media-playback-pause')) self.pause_button.set_tooltip_text('Pause') self.pause_button.set_sensitive(False) self.pause_button.connect('clicked', self.on_pause_button_clicked) button_box.add(self.pause_button) ## Stop self.stop_button = Gtk.Button() self.stop_button.set_image(Gtk.Image(icon_name='media-playback-stop')) self.stop_button.set_tooltip_text('Stop') self.stop_button.set_sensitive(False) self.stop_button.connect('clicked', self.on_stop_button_clicked) button_box.add(self.stop_button) ### Path Tab path_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) path_page.set_border_width(10) bot_notebook.append_page(path_page, Gtk.Label('Path')) ## Movement path_page.add(Gtk.Label('<b>Movement</b>', xalign=0, use_markup=True)) button_box = ButtonBox(orientation=Gtk.Orientation.VERTICAL, centered=True) path_page.add(button_box) # Up up_button = Gtk.Button() up_button.set_image(Gtk.Image(icon_name='go-up')) up_button.connect('clicked', lambda button: self.path_listbox.append_text('Move(UP)')) button_box.add(up_button) # Left left_button = Gtk.Button() left_button.set_image(Gtk.Image(icon_name='go-previous')) left_button.connect('clicked', lambda button: self.path_listbox.append_text('Move(LEFT)')) hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=40) hbox.add(left_button) button_box.add(hbox) # Right right_button = Gtk.Button() right_button.set_image(Gtk.Image(icon_name='go-next')) right_button.connect('clicked', lambda buton: self.path_listbox.append_text('Move(RIGHT)')) hbox.add(right_button) # Down down_button = Gtk.Button() down_button.set_image(Gtk.Image(icon_name='go-down')) down_button.connect('clicked', lambda button: self.path_listbox.append_text('Move(DOWN)')) button_box.add(down_button) ## Action path_page.add(Gtk.Label('<b>Action</b>', xalign=0, use_markup=True)) stack_listbox = StackListBox() path_page.add(stack_listbox) ## Enclos pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/enclos.png'), 24, 24) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Enclos') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Location widget.add(Gtk.Label('<b>Location</b>', xalign=0, use_markup=True)) self.enclos_location_combo = CustomComboBox(data.Enclos, sort=True) self.enclos_location_combo.set_margin_left(10) widget.add(self.enclos_location_combo) # Type widget.add(Gtk.Label('<b>Type</b>', xalign=0, use_markup=True)) self.enclos_type_combo = CustomComboBox(data.EnclosType, sort=True) self.enclos_type_combo.set_margin_left(10) widget.add(self.enclos_type_combo) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Enclos(location=%s,type=%s)' % (self.enclos_location_combo.get_active_text(), self.enclos_type_combo.get_active_text()))) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Zaap pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/zaap.png'), 24, 24) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Zaap') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # From widget.add(Gtk.Label('<b>From</b>', xalign=0, use_markup=True)) self.zaap_from_combo = CustomComboBox(data.Zaap['From'], sort=True) self.zaap_from_combo.set_margin_left(10) self.zaap_from_combo.connect('changed', lambda combo: combo.sync_with_combo(self.zaap_to_combo) ) widget.add(self.zaap_from_combo) # To widget.add(Gtk.Label('<b>To</b>', xalign=0, use_markup=True)) self.zaap_to_combo = CustomComboBox(data.Zaap['To'], sort=True) self.zaap_to_combo.set_margin_left(10) self.zaap_to_combo.connect('changed', lambda combo: combo.sync_with_combo(self.zaap_from_combo) ) widget.add(self.zaap_to_combo) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Zaap(from=%s,to=%s)' % (self.zaap_from_combo.get_active_text(), self.zaap_to_combo.get_active_text()))) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Zaapi pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/destination.png'), 24, 24) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Zaapi') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # From widget.add(Gtk.Label('<b>From</b>', xalign=0, use_markup=True)) self.zaapi_from_combo = CustomComboBox(data.Zaapi['From'], sort=True) self.zaapi_from_combo.set_margin_left(10) self.zaapi_from_combo.connect('changed', lambda combo: combo.sync_with_combo(self.zaapi_to_combo, use_contains=True) ) widget.add(self.zaapi_from_combo) # To widget.add(Gtk.Label('<b>To</b>', xalign=0, use_markup=True)) self.zaapi_to_combo = CustomComboBox(data.Zaapi['To'], sort=True) self.zaapi_to_combo.set_margin_left(10) self.zaapi_to_combo.connect('changed', lambda combo: combo.sync_with_combo(self.zaapi_from_combo, use_contains=True) ) widget.add(self.zaapi_to_combo) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Zaapi(from=%s,to=%s)' % (self.zaapi_from_combo.get_active_text(), self.zaapi_to_combo.get_active_text()))) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Collect pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/miner.png'), 24, 24) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Collect') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Map widget.add(Gtk.Label('<b>Map</b>', xalign=0, use_markup=True)) self.collect_map_combo = CustomComboBox(maps.load(), sort=True) self.collect_map_combo.set_margin_left(10) widget.add(self.collect_map_combo) # Store Path widget.add(Gtk.Label('<b>Store Path</b>', xalign=0, use_markup=True)) # Combo hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.set_margin_left(10) self.collect_sp_combo_radio = Gtk.RadioButton() self.collect_sp_combo_radio.set_active(True) hbox.add(self.collect_sp_combo_radio) self.collect_sp_combo = CustomComboBox(data.BankPath, sort=True) self.collect_sp_combo.connect('changed', lambda combo: self.collect_sp_combo_radio.set_active(True)) hbox.pack_start(self.collect_sp_combo, True, True, 0) widget.add(hbox) # FileChooserButton hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.set_margin_left(10) self.collect_sp_filechooser_radio = Gtk.RadioButton(group=self.collect_sp_combo_radio) hbox.add(self.collect_sp_filechooser_radio) self.collect_sp_filechooserbutton = FileChooserButton(title='Choose store path', filter=('Store Path', '*.path')) self.collect_sp_filechooserbutton.set_current_folder(tools.get_full_path('paths')) self.collect_sp_filechooserbutton.get_children()[0].get_children()[0].get_children()[1].set_max_width_chars(12) self.collect_sp_filechooserbutton.connect('file-set', lambda filechooserbutton: self.collect_sp_filechooser_radio.set_active(True)) hbox.pack_start(self.collect_sp_filechooserbutton, True, True, 0) widget.add(hbox) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Collect(map=%s,store_path=%s)' % (self.collect_map_combo.get_active_text(), self.collect_sp_combo.get_active_text() if self.collect_sp_combo_radio.get_active() else (self.collect_sp_filechooserbutton.get_filename() or 'None').replace(tools.get_full_path('') + '/', '')))) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Click pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/arrow.png'), 24, 24) #pixbuf = Gdk.Cursor(Gdk.CursorType.ARROW).get_image().scale_simple(24, 24, GdkPixbuf.InterpType.BILINEAR) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Click') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Twice hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('<b>Twice</b>', xalign=0, use_markup=True)) self.click_twice_switch = Gtk.Switch() hbox.pack_end(self.click_twice_switch, False, False, 0) widget.add(hbox) # Location hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('<b>Location</b>', xalign=0, use_markup=True)) cursor_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/crosshair.png'), 16, 16) #cursor_pixbuf = Gdk.Cursor(Gdk.CursorType.CROSSHAIR).get_image().scale_simple(18, 18, GdkPixbuf.InterpType.BILINEAR) self.select_button = Gtk.Button() self.select_button.set_size_request(40, -1) self.select_button.set_tooltip_text('Select') self.select_button.set_image(Gtk.Image(pixbuf=cursor_pixbuf)) self.select_button.connect('clicked', self.on_select_button_clicked) hbox.pack_end(self.select_button, False, False, 0) widget.add(hbox) ## Scroll pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/scroll.png'), 24, 24) #pixbuf = Gdk.Cursor(Gdk.CursorType.SB_V_DOUBLE_ARROW).get_image().scale_simple(18, 18, GdkPixbuf.InterpType.BILINEAR) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Scroll') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Direction hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('<b>Direction</b>', xalign=0, use_markup=True)) self.scroll_direction_combo = CustomComboBox(['up', 'down']) self.scroll_direction_combo.set_active(1) hbox.pack_end(self.scroll_direction_combo, False, False, 0) widget.add(hbox) # Times hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('<b>Times</b>', xalign=0, use_markup=True)) self.scroll_spin_button = SpinButton(min=1, max=10) hbox.pack_end(self.scroll_spin_button, False, False, 0) widget.add(hbox) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Scroll(direction=%s,times=%s)' % (self.scroll_direction_combo.get_active_text(), self.scroll_spin_button.get_value_as_int()))) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Wait pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/hourglass.png'), 24, 24) image = Gtk.Image(pixbuf=pixbuf) label = ImageLabel(image, 'Wait') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Duration hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) label = Gtk.Label('<b>Duration</b>', xalign=0, use_markup=True) label.set_tooltip_text('(in seconds)') self.duration_radio = Gtk.RadioButton() self.duration_radio.add(label) hbox.add(self.duration_radio) self.duration_spin_button = SpinButton(min=1, max=60) self.duration_spin_button.connect('value-changed', lambda button: self.duration_radio.set_active(True)) hbox.pack_end(self.duration_spin_button, False, False, 0) widget.add(hbox) # Pause Bot self.pause_bot_radio = Gtk.RadioButton(group=self.duration_radio) self.pause_bot_radio.add(Gtk.Label('<b>Pause Bot</b>', xalign=0, use_markup=True)) widget.add(self.pause_bot_radio) # Monitor Game Screen self.monitor_game_screen_radio = Gtk.RadioButton(group=self.duration_radio) self.monitor_game_screen_radio.add(Gtk.Label('<b>Until map/screen changes</b>', xalign=0, use_markup=True)) widget.add(self.monitor_game_screen_radio) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', self.on_wait_add_button_clicked) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Keyboard image = Gtk.Image(icon_name='input-keyboard', icon_size=Gtk.IconSize.LARGE_TOOLBAR) label = ImageLabel(image, 'Keyboard') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Press Key hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) self.press_key_radio = Gtk.RadioButton() self.press_key_radio.add(Gtk.Label('<b>Press Key</b>', xalign=0, use_markup=True)) hbox.add(self.press_key_radio) self.key_label = Gtk.Label() hbox.add(self.key_label) widget.add(hbox) self.keys_combo = CustomComboBox(data.KeyboardShortcuts, sort=True) self.keys_combo.set_margin_left(10) self.keys_combo.connect('changed', lambda combo: ( self.key_label.set_text('(' + data.KeyboardShortcuts[combo.get_active_text()] + ')'), self.press_key_radio.set_active(True) ) ) widget.add(self.keys_combo) # Type Text self.type_text_radio = Gtk.RadioButton(group=self.press_key_radio) self.type_text_radio.add(Gtk.Label('<b>Type Text</b>', xalign=0, use_markup=True)) widget.add(self.type_text_radio) self.type_text_entry = Gtk.Entry(placeholder_text='Text') self.type_text_entry.set_margin_left(10) self.type_text_entry.set_width_chars(10) self.type_text_entry.connect('focus-in-event', lambda entry, event: self.type_text_radio.set_active(True)) widget.add(self.type_text_entry) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', self.on_keyboard_add_button_clicked) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Connect image = Gtk.Image(icon_name='network-wired', icon_size=Gtk.IconSize.LARGE_TOOLBAR) label = ImageLabel(image, 'Connect') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Account widget.add(Gtk.Label('<b>Account</b>', xalign=0, use_markup=True)) self.connect_accounts_combo = TextValueComboBox(accounts_list, model=Gtk.ListStore(str, int), text_key='login', value_key='id', sort_key='position') self.connect_accounts_combo.set_margin_left(10) widget.add(self.connect_accounts_combo) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Connect(account_id=%s)' % self.connect_accounts_combo.get_active_value())) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Disconnect image = Gtk.Image(icon_name='network-idle', icon_size=Gtk.IconSize.LARGE_TOOLBAR) label = ImageLabel(image, 'Disconnect') widget = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) stack_listbox.append(label, widget) # Exit Game hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) widget.add(hbox) hbox.add(Gtk.Label('<b>Exit Game</b>', xalign=0, use_markup=True)) self.exit_game_switch = Gtk.Switch() hbox.pack_end(self.exit_game_switch, False, False, 0) # Add add_button = Gtk.Button('Add') add_button.connect('clicked', lambda button: self.path_listbox.append_text('Disconnect(%s)' % self.exit_game_switch.get_active())) button_box = ButtonBox(centered=True) button_box.add(add_button) widget.add(button_box) ## Separator path_page.add(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL, margin=5)) ## Listbox self.path_listbox = CustomListBox(parent=self) path_page.pack_end(self.path_listbox, True, True, 0) # Load load_path_button = Gtk.Button() load_path_button.set_tooltip_text('Load') load_path_button.set_image(Gtk.Image(icon_name='document-open')) load_path_button.connect('clicked', self.on_load_path_button_clicked) self.path_listbox.add_button(load_path_button) # Save self.save_path_button = Gtk.Button() self.save_path_button.set_tooltip_text('Save') self.save_path_button.set_sensitive(False) self.save_path_button.set_image(Gtk.Image(icon_name='document-save-as')) self.save_path_button.connect('clicked', self.on_save_path_button_clicked) self.path_listbox.add_button(self.save_path_button) self.path_listbox.on_add(self.on_path_listbox_add) self.path_listbox.on_delete(self.on_path_listbox_delete) ### Map Tab map_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) map_page.set_border_width(10) bot_notebook.append_page(map_page, Gtk.Label('Map')) ## View hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) hbox.add(Gtk.Label('<b>View</b>', xalign=0, use_markup=True)) map_page.add(hbox) # Options menu_image = MenuImage() hbox.add(menu_image) options_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) menu_image.set_widget(options_box) # Use data colors use_data_colors_check = Gtk.CheckButton('Use data colors') use_data_colors_check.connect('clicked', lambda button: self.map_view.set_use_origin_colors(button.get_active())) options_box.add(use_data_colors_check) # Add borders add_borders_check = Gtk.CheckButton('Add borders') add_borders_check.connect('clicked', lambda button: self.map_view.set_add_borders(button.get_active())) options_box.add(add_borders_check) # Show selected data only self.show_selected_data_only_check = Gtk.CheckButton('Show selected data only') self.show_selected_data_only_check.connect('clicked', self.on_show_selected_data_only_check_clicked) options_box.add(self.show_selected_data_only_check) # Map View self.map_view = MiniMap() map_page.pack_start(self.map_view, True, True, 0) ## Data map_page.add(Gtk.Label('<b>Data</b>', xalign=0, use_markup=True)) self.map_data_listbox = CustomListBox(parent=self, allow_moving=False) map_page.pack_start(self.map_data_listbox, True, True, 0) # Select self.select_resource_button = Gtk.Button() self.select_resource_button.set_tooltip_text('Select resource') self.select_resource_button.set_image(Gtk.Image(pixbuf=cursor_pixbuf)) self.select_resource_button.connect('clicked', self.on_select_resource_button_clicked) self.map_data_listbox.add_button(self.select_resource_button) # Simulate click self.simulate_resource_click_button = Gtk.Button() self.simulate_resource_click_button.set_tooltip_text('Simulate click') self.simulate_resource_click_button.set_sensitive(False) pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(tools.get_full_path('icons/hand.png'), 16, 16) self.simulate_resource_click_button.set_image(Gtk.Image(pixbuf=pixbuf)) self.simulate_resource_click_button.connect('clicked', self.on_simulate_resource_click_button_clicked) self.map_data_listbox.add_button(self.simulate_resource_click_button) # Edit edit_map_button = MenuButton(icon_name='document-edit-symbolic') edit_map_button.set_tooltip_text('Edit') self.map_data_listbox.add_button(edit_map_button) button_box = ButtonBox(linked=True) edit_map_button.add(button_box) # Load load_map_button = Gtk.Button() load_map_button.set_tooltip_text('Load') load_map_button.set_image(Gtk.Image(icon_name='document-open')) load_map_button.connect('clicked', self.on_load_map_button_clicked) button_box.add(load_map_button) # Delete delete_map_button = Gtk.Button() delete_map_button.set_tooltip_text('Delete') delete_map_button.set_image(Gtk.Image(icon_name='edit-delete')) delete_map_button.connect('clicked', self.on_delete_map_button_clicked) button_box.add(delete_map_button) # Save self.save_map_button = Gtk.Button() self.save_map_button.set_tooltip_text('Save') self.save_map_button.set_sensitive(False) self.save_map_button.set_image(Gtk.Image(icon_name='document-save-as')) self.save_map_button.connect('clicked', self.on_save_map_button_clicked) self.map_data_listbox.add_button(self.save_map_button) self.map_data_listbox.on_add(self.on_map_data_listbox_add) self.map_data_listbox.on_delete(self.on_map_data_listbox_delete) self.map_data_listbox.on_activate(self.on_map_data_listbox_activate) def on_show_selected_data_only_check_clicked(self, button): self.map_view.clear() if button.get_active(): selected_row = self.map_data_listbox.listbox.get_selected_row() if selected_row: text = self.map_data_listbox.get_row_text(selected_row) point = maps.to_array(text) self.map_view.add_point(point, 'Resource', MiniMap.point_colors['Resource']) else: points = [] for row in self.map_data_listbox.get_rows(): text = self.map_data_listbox.get_row_text(row) point = maps.to_array(text) points.append(point) self.map_view.add_points(points, 'Resource', MiniMap.point_colors['Resource']) def on_simulate_resource_click_button_clicked(self, button): selected_row = self.map_data_listbox.listbox.get_selected_row() if selected_row: text = self.map_data_listbox.get_row_text(selected_row) data = maps.to_array(text) x, y, width, height, color = (data['x'], data['y'], data['width'], data['height'], data['color']) #print('x: %d, y: %d, width: %d, height: %d' % (x, y, width, height)) # adjust for game area if self.game_window and not self.game_window.is_destroyed() and self.game_window_location: game_x, game_y, game_width, game_height = self.game_window_location #print('game_x: %d, game_y: %d, game_width: %d, game_height: %d' % (game_x, game_y, game_width, game_height)) click_x, click_y = tools.adjust_click_position(x, y, width, height, game_x, game_y, game_width, game_height) else: click_x = x click_y = y # perform click self.debug('Simulate click on x: %d, y: %d' % (click_x, click_y)) tools.perform_click(click_x, click_y) def on_load_map_button_clicked(self, button): dialog = LoadMapDialog(self) dialog.run() def on_delete_map_button_clicked(self, button): dialog = DeleteMapDialog(self) dialog.run() def on_save_map_button_clicked(self, button): dialog = SaveMapDialog(self) dialog.run() def add_map_data(self, location): # append to listbox text = '{"x": %d, "y": %d, "width": %d, "height": %d, "color": "%s"}' % location self.map_data_listbox.append_text(text) # append to view point = maps.to_array(text) self.map_view.add_point(point, 'Resource', MiniMap.point_colors['Resource']) self.select_resource_button.set_sensitive(True) self.set_cursor(Gdk.Cursor(Gdk.CursorType.ARROW)) def on_select_resource_button_clicked(self, button): button.set_sensitive(False) self.set_cursor(Gdk.Cursor(Gdk.CursorType.CROSSHAIR)) Thread(target=self.wait_for_click, args=(self.add_map_data, self.game_window_location)).start() def on_map_data_listbox_add(self): if not self.save_map_button.get_sensitive(): self.save_map_button.set_sensitive(True) if not self.simulate_resource_click_button.get_sensitive(): self.simulate_resource_click_button.set_sensitive(True) def on_map_data_listbox_delete(self, row_index): if self.show_selected_data_only_check.get_active(): self.map_view.clear() else: self.map_view.remove_point(row_index) self.simulate_resource_click_button.set_sensitive(False) if self.map_data_listbox.is_empty(): self.save_map_button.set_sensitive(False) def on_map_data_listbox_activate(self): if not self.simulate_resource_click_button.get_sensitive(): self.simulate_resource_click_button.set_sensitive(True) if self.show_selected_data_only_check.get_active(): self.on_show_selected_data_only_check_clicked(self.show_selected_data_only_check) def on_path_listbox_add(self): if not self.save_path_button.get_sensitive(): self.save_path_button.set_sensitive(True) def on_path_listbox_delete(self, row_index): if self.path_listbox.is_empty(): self.save_path_button.set_sensitive(False) def on_load_path_button_clicked(self, button): dialog = OpenFileDialog('Load Path', self, ('Bot Path', '*.path')) dialog.set_current_folder(tools.get_full_path('paths')) response = dialog.run() if response == Gtk.ResponseType.OK: # read file path = tools.read_file(dialog.get_filename()) # append to path listbox for line in path.splitlines(): self.path_listbox.append_text(line) dialog.destroy() def on_save_path_button_clicked(self, button): dialog = SaveFileDialog('Save as', self, ('Bot Path', '*.path')) dialog.set_current_folder(tools.get_full_path('paths')) dialog.set_current_name('path_' + tools.get_date_time() + '.path') response = dialog.run() if response == Gtk.ResponseType.OK: # get all rows text text = '' for row in self.path_listbox.get_rows(): text += self.path_listbox.get_row_text(row) + '\n' # save it to file tools.save_text_to_file(text, dialog.get_filename()) dialog.destroy() def on_wait_add_button_clicked(self, button): if self.pause_bot_radio.get_active(): self.path_listbox.append_text('Pause()') elif self.monitor_game_screen_radio.get_active(): self.path_listbox.append_text('MonitorGameScreen()') else: self.path_listbox.append_text('Wait(%s)' % self.duration_spin_button.get_value_as_int()) def on_keyboard_add_button_clicked(self, button): if self.press_key_radio.get_active(): selected = self.keys_combo.get_active_text() self.path_listbox.append_text('PressKey(%s)' % parser.parse_data(data.KeyboardShortcuts, selected)) else: self.path_listbox.append_text('TypeText(%s)' % self.type_text_entry.get_text()) def wait_for_click(self, callback, game_location=None): # wait for click tools.wait_for_mouse_event('left_down') # get mouse position & screen size x, y = tools.get_mouse_position() width, height = tools.get_screen_size() # get pixel color color = tools.get_pixel_color(x, y) # adjust location to game window if game_location is not None: # get game area location game_x, game_y, game_width, game_height = game_location #print('x: %d, y: %d, game_x: %d, game_y: %d, game_width: %d, game_height: %d' % (x, y, game_x, game_y, game_width, game_height)) # scale to game area if tools.position_is_inside_bounds(x, y, game_x, game_y, game_width, game_height): # position is inside game area, so we fit x & y to it x = x - game_x y = y - game_y width = game_width height = game_height # execute callback GObject.idle_add(callback, (x, y, width, height, color)) def add_click(self, location): x, y, width, height, color = location twice = self.click_twice_switch.get_active() self.path_listbox.append_text('Click(x=%d,y=%d,width=%d,height=%d,twice=%s)' % (x, y, width, height, twice)) self.select_button.set_sensitive(True) self.set_cursor(Gdk.Cursor(Gdk.CursorType.ARROW)) def set_cursor(self, cursor): window = self.get_window() # Gdk.get_default_root_window() window.set_cursor(cursor) def on_select_button_clicked(self, button): button.set_sensitive(False) self.set_cursor(Gdk.Cursor(Gdk.CursorType.CROSSHAIR)) Thread(target=self.wait_for_click, args=(self.add_click, self.game_window_location)).start() def on_start_button_clicked(self, button): if self.game_window is None: AlertDialog(self, 'Please select a game window') elif self.game_window.is_destroyed(): AlertDialog(self, 'Chosen game window was destroyed') elif not self.bot_path: AlertDialog(self, 'Please select a bot path') else: # ensure that game window is in the right place self.move_resize_game_window(self.game_window_location) # start bot thread if self.bot_thread is None or not self.bot_thread.isAlive(): # get thread parameters start_from_step = self.step_spin_button.get_value_as_int() repeat_path = self.repeat_spin_button.get_value_as_int() if self.repeat_switch.get_active() else 1 if self.connect_to_account_switch.get_active(): account_id = self.accounts_combo.get_active_value() disconnect_after = self.disconnect_after_switch.get_active() else: account_id = None disconnect_after = False # run thread self.bot_thread = BotThread(self, self.game_window_location, start_from_step, repeat_path, account_id, disconnect_after) self.bot_thread.start() self.settings_button.set_sensitive(False) self.bot_config_widgets.set_sensitive(False) # resume bot thread if paused else: self.bot_thread.resume(self.game_window_location) # show bot state & debug tabs self.log_notebook.set_current_page(1) if self.settings['Debug']['Enabled']: self.config_notebook.set_current_page(1) # enable/disable buttons self.start_button.set_image(Gtk.Image(file=tools.get_full_path('icons/loader.gif'))) self.start_button.set_sensitive(False) self.pause_button.set_sensitive(True) self.stop_button.set_sensitive(True) def set_internet_state(self, state): if state: self.start_button.set_image(Gtk.Image(file=tools.get_full_path('icons/loader.gif'))) else: self.log(tools.print_internet_state(state), LogType.Error) self.start_button.set_image(Gtk.Image(icon_name='network-error')) def set_buttons_to_paused(self): self.start_button.set_tooltip_text('Resume') self.start_button.set_image(Gtk.Image(icon_name='media-skip-forward')) self.start_button.set_sensitive(True) self.pause_button.set_sensitive(False) def pause_bot(self): if self.bot_thread and self.bot_thread.isAlive() and self.bot_thread.pause_event.isSet(): self.bot_thread.pause() def on_pause_button_clicked(self, button): self.pause_bot() def reset_buttons(self): self.start_button.set_tooltip_text('Start') self.start_button.set_image(Gtk.Image(icon_name='media-playback-start')) self.start_button.set_sensitive(True) self.stop_button.set_sensitive(False) self.pause_button.set_sensitive(False) self.settings_button.set_sensitive(True) self.bot_config_widgets.set_sensitive(True) def on_stop_button_clicked(self, button): if self.bot_thread and self.bot_thread.isAlive(): self.bot_thread.stop() self.reset_buttons() def on_bot_path_changed(self, filechooserbutton): self.bot_path = filechooserbutton.get_filename() def populate_game_window_combo(self): self.game_window_combo_ignore_change = True self.game_window_combo.remove_all() self.game_windowList = tools.get_game_window_list() count = len(self.game_windowList) if count == 0: self.debug('Populate game window combobox, no window found', DebugLevel.High) else: self.debug('Populate game window combobox, %d window found' % count, DebugLevel.High) for window_name in self.game_windowList: self.game_window_combo.append_text(window_name) self.game_window_combo_ignore_change = False def focus_game(self): if self.game_window and not self.game_window.is_destroyed(): #self.debug('Focus game', DebugLevel.High) # activate & focus game window tools.activate_window(self.game_window) #self.game_window.focus(0) def move_resize_game_window(self, location): if self.game_window and not self.game_window.is_destroyed() and location: x, y, width, height = location self.debug('Move & resize game window (x: %d, y: %d, width: %d, height: %d)' % (x, y, width, height), DebugLevel.Low) self.game_window.unmaximize() self.game_window.move_resize(x, y, width, height) self.game_window.show() # force show (when minimized) tools.activate_window(self.game_window) def bind_game_window(self, window_xid): self.debug('Bind game window (id: %d)' % window_xid, DebugLevel.Low) self.game_window = tools.get_game_window(window_xid) if self.game_window: bot_width, bot_height = self.get_size() bot_decoration_height = self.get_titlebar().get_allocated_height() screen_width, screen_height = tools.get_screen_size() game_window_left_margin = 1 game_window_decoration_height = self.settings['Game']['WindowDecorationHeight'] if self.settings['Game']['UseCustomWindowDecorationHeight'] else tools.get_game_window_decoration_height(window_xid) game_window_width = screen_width - bot_width - game_window_left_margin game_window_height = bot_height if self.settings['Game']['UseCustomWindowDecorationHeight'] else bot_height + bot_decoration_height - game_window_decoration_height if game_window_width > 900: game_window_width = 900 bot_x_position = screen_width / 2 - (bot_width + game_window_width) / 2 bot_y_position = screen_height / 2 - bot_height / 2 game_window_x_position = bot_x_position + bot_width + game_window_left_margin game_window_y_position = bot_y_position + game_window_decoration_height # save game window location self.game_window_location = (int(game_window_x_position), int(game_window_y_position), int(game_window_width), int(game_window_height)) # move bot & game window self.move(bot_x_position, bot_y_position) self.move_resize_game_window(self.game_window_location) # enable/disable widgets self.refresh_button.hide() self.unbind_button.show() self.game_window_combo.set_sensitive(False) self.take_screenshot_button.set_sensitive(True) def on_game_window_combo_changed(self, combo): if self.game_windowList and not self.game_window_combo_ignore_change: # get selected game window selected = combo.get_active_text() window_xid = self.game_windowList[selected] # bind it self.bind_game_window(window_xid) def unbind_game_window(self): self.debug('Unbind game window') if self.settings['Game']['KeepOpen']: self.debug('Keep game window open') elif self.game_window and not self.game_window.is_destroyed(): self.game_window.destroy() self.game_window = None self.game_window_location = None self.debug('Game window unbinded') # enable/disable widgets self.unbind_button.hide() self.refresh_button.show() self.game_window_combo.set_sensitive(True) self.populate_game_window_combo() self.take_screenshot_button.set_sensitive(False) def on_unbind_button_clicked(self, button): self.unbind_game_window() def on_refresh_button_clicked(self, button): self.populate_game_window_combo() # Override the default handler for the delete-event signal def do_delete_event(self, event): # Show our message dialog dialog = Gtk.MessageDialog(text='Are you sure you want to quit?', transient_for=self, buttons=Gtk.ButtonsType.OK_CANCEL, message_type=Gtk.MessageType.QUESTION) response = dialog.run() dialog.destroy() # We only terminate when the user presses the OK button if response == Gtk.ResponseType.OK: # stop bot thread if self.bot_thread and self.bot_thread.isAlive(): self.bot_thread.stop() return False # Otherwise we keep the application open return True def main(self): Gtk.main()