diff --git a/b_asic/schedule.py b/b_asic/schedule.py
index 347faaf5ecd9b476d4bc62bfc64f43251840d32e..6d05778ef35e08a79d51b4a20f24590b76abc1c3 100644
--- a/b_asic/schedule.py
+++ b/b_asic/schedule.py
@@ -42,6 +42,16 @@ _LATENCY_COLOR = tuple(c / 255 for c in LATENCY_COLOR)
 _SIGNAL_COLOR = tuple(c / 255 for c in SIGNAL_COLOR)
 
 
+def _laps_default():
+    """Default value for _laps. Cannot use lambda."""
+    return 0
+
+
+def _y_locations_default():
+    """Default value for _y_locations. Cannot use lambda."""
+    return None
+
+
 class Schedule:
     """
     Schedule of an SFG with scheduled Operations.
@@ -89,9 +99,9 @@ class Schedule:
         self._original_sfg = sfg()  # Make a copy
         self._sfg = sfg
         self._start_times = {}
-        self._laps = defaultdict(lambda: 0)
+        self._laps = defaultdict(_laps_default)
         self._cyclic = cyclic
-        self._y_locations = defaultdict(lambda: None)
+        self._y_locations = defaultdict(_y_locations_default)
         if scheduling_algorithm == "ASAP":
             self._schedule_asap()
         elif scheduling_algorithm == "provided":
diff --git a/b_asic/scheduler_gui/main_window.py b/b_asic/scheduler_gui/main_window.py
index b65233d31b2516a48ede6d14f51a6699f0ff1664..9478b2c96db0113bd7026de3ff3d05d1723575e8 100644
--- a/b_asic/scheduler_gui/main_window.py
+++ b/b_asic/scheduler_gui/main_window.py
@@ -9,6 +9,7 @@ Start main-window with ``start_gui()``.
 """
 import inspect
 import os
+import pickle
 import sys
 import webbrowser
 from collections import deque
@@ -118,6 +119,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
         self.setupUi(self)
         self._read_settings()
         self._init_ui()
+        self._file_name = None
 
         # Recent files
         self._max_recent_files = 4
@@ -134,6 +136,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
         self.menu_load_from_file.triggered.connect(self._load_schedule_from_pyfile)
         self.menu_load_from_file.setIcon(get_icon('import'))
         self.menu_open.setIcon(get_icon('open'))
+        self.menu_open.triggered.connect(self.open_schedule)
         self.menu_close_schedule.triggered.connect(self.close_schedule)
         self.menu_close_schedule.setIcon(get_icon('close'))
         self.menu_save.triggered.connect(self.save)
@@ -354,6 +357,8 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
         del module
         settings = QSettings()
         settings.setValue("scheduler/last_opened_file", abs_path_filename)
+        self._file_name = abs_path_filename
+        self._toggle_file_loaded(True)
 
     @Slot()
     def close_schedule(self) -> None:
@@ -381,6 +386,7 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
             self._schedule = None
             self.info_table_clear()
             self.update_statusbar("Closed schedule")
+            self._toggle_file_loaded(False)
 
     @Slot()
     def save(self) -> None:
@@ -390,7 +396,13 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
         SLOT() for SIGNAL(menu_save.triggered)
         """
         # TODO: all
-        self._print_button_pressed("save_schedule()")
+        if self._file_name is None:
+            self.save_as()
+            return
+        self._schedule._sfg._graph_id_generator = None
+        self._schedule._original_sfg._graph_id_generator = None
+        with open(self._file_name, 'wb') as f:
+            pickle.dump(self._schedule, f)
         self.update_statusbar(self.tr("Schedule saved successfully"))
 
     @Slot()
@@ -401,9 +413,53 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
         SLOT() for SIGNAL(menu_save_as.triggered)
         """
         # TODO: Implement
-        self._print_button_pressed("save_schedule()")
+        filename, extension = QFileDialog.getSaveFileName(
+            self, 'Save File', '.', filter=self.tr("B-ASIC schedule (*.bsc)")
+        )
+        if not filename:
+            return
+        if not filename.endswith('.bsc'):
+            filename += '.bsc'
+        self._file_name = filename
+        self._schedule._sfg._graph_id_generator = None
+        self._schedule._original_sfg._graph_id_generator = None
+        with open(self._file_name, 'wb') as f:
+            pickle.dump(self._schedule, f)
         self.update_statusbar(self.tr("Schedule saved successfully"))
 
+    @Slot()
+    def open_schedule(self) -> None:
+        """
+        Open a schedule.
+
+        SLOT() for SIGNAL(menu_open.triggered)
+        """
+        # TODO: all
+        last_file = QStandardPaths.standardLocations(QStandardPaths.HomeLocation)[0]
+
+        abs_path_filename, accepted = QFileDialog.getOpenFileName(
+            self,
+            self.tr("Open schedule file"),
+            last_file,
+            self.tr("B-ASIC schedule (*.bsc)"),
+        )
+
+        if not abs_path_filename or not accepted:
+            return
+        self._open_schedule_file(abs_path_filename)
+
+    def _open_schedule_file(self, abs_path_filename: str):
+        self._file_name = abs_path_filename
+        self._add_recent_file(abs_path_filename)
+
+        with open(self._file_name, 'rb') as f:
+            schedule = pickle.load(f)
+        self.open(schedule)
+        settings = QSettings()
+        settings.setValue("scheduler/last_opened_file", self._file_name)
+        self._toggle_file_loaded(True)
+        self.update_statusbar(self.tr("Schedule loaded successfully"))
+
     @Slot(bool)
     def show_info_table(self, checked: bool) -> None:
         """
@@ -426,6 +482,10 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
         else:
             self.splitter.moveSplitter(max_, 1)
 
+    def _toggle_file_loaded(self, enable: bool):
+        self.menu_save.setEnabled(enable)
+        self.menu_save_as.setEnabled(enable)
+
     @Slot(bool)
     def hide_exit_dialog(self, checked: bool) -> None:
         """
@@ -732,7 +792,10 @@ class ScheduleMainWindow(QMainWindow, Ui_MainWindow):
                     self._recent_files_actions[i].setVisible(False)
 
     def _open_recent_file(self, action):
-        self._load_from_file(action.data().filePath())
+        if action.data().filePath().endswith('.bsc'):
+            self._open_schedule_file(action.data().filePath())
+        else:
+            self._load_from_file(action.data().filePath())
 
     def _add_recent_file(self, module):
         settings = QSettings()