iam-git / WellMet (public) (License: MIT) (since 2021-08-31) (hash sha1)
WellMet is pure Python framework for spatial structural reliability analysis. Or, more specifically, for "failure probability estimation and detection of failure surfaces by adaptive sequential decomposition of the design domain".

/qt_gui/qt_gui.py (833fe656bac350dcc896199dff00a0be7b9700f8) (22208 bytes) (mode 100644) (type blob)

#!/usr/bin/env python
# coding: utf-8

import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
from pyqtgraph.Qt import QtCore

from pyqtgraph import console

import numpy as np
from . import qt_graph_widgets
from .. import reader


### Define a top-level widget to hold everything
class QtGuiWindow(QtGui.QMainWindow):
    #č box_runned dublikuje slice_changed
    # do not redraw twice!
    box_runned = QtCore.pyqtSignal()
    space_changed = QtCore.pyqtSignal()
    slice_changed = QtCore.pyqtSignal()
    redraw_called = QtCore.pyqtSignal()
    estimation_added = QtCore.pyqtSignal()
    
    # INHERITED by gl_plot
    # snad pri vykreslování argy-kvargy nevádí
    def __init__(self, sample_box, space='R', *args, **kwargs):
        
        #E former self.w = QtGui.QMainWindow()
        self.app = pg.mkQApp()
        super().__init__()
        try:
            self.setWindowTitle("%sD: %s" %(sample_box.nvar, sample_box.gm_signature))
        except:
            self.setWindowTitle("%sD nodes" % sample_box.nvar)
        
        # for debug
        # container for errors
        # to trace errors
        self.errors = []
        
        self.kwargs = kwargs
        self.sample_box = sample_box
        #sample_box.sample_box._log = self.logger
        self.last_shot = None
        try:
            self.space = self.sample_box.tri_space
        except AttributeError:
            self.space = space
        
        # "zapnuté" prostory
        #self.spaces = ['R', 'aR', 'Rn', 'aRn', 'P', 'aP', 'GK', 'aGK', 'G', 'aG', 'U', 'aU']
        self.spaces = ['R', 'aR', 'Rn', 'aRn', 'P', 'GK', 'G', 'aG', 'U', 'aU']

        # initialize central widget
        self.initialize_central_widget()
        
        # common setup
        self.setup()

        self.plot_setup()
        
        ## Display the widget as a new window
        self.show()

        #
        self.redraw_called.emit()
        
        ## Start the Qt event loop
        self.app.exec_()
    
    def setup_menubar(self):    
        self.bar = self.menuBar()
        self.file_menu = self.bar.addMenu("File")
        #č kontejner jen aby Python mně Cečkové objekty nevymazal
        self.file_actions = [] 
        load_coordinates_action = QtGui.QAction("Load coordinates", self)
        load_coordinates_action.triggered.connect(self.load_coordinates)
        self.file_menu.addAction(load_coordinates_action)
        self.file_actions.append(load_coordinates_action)
        
        export_coordinates_action = QtGui.QAction("Export coordinates", self)
        export_coordinates_action.triggered.connect(self.export_coordinates)
        self.file_menu.addAction(export_coordinates_action)
        self.file_actions.append(export_coordinates_action)
        
        import_samples_action = QtGui.QAction("Import samples", self)
        import_samples_action.triggered.connect(self.import_samples)
        self.file_menu.addAction(import_samples_action)
        self.file_actions.append(import_samples_action)
        
        export_samples_action = QtGui.QAction("Export samples", self)
        export_samples_action.triggered.connect(self.export_samples)
        self.file_menu.addAction(export_samples_action)
        self.file_actions.append(export_samples_action)
        
        self.view = self.bar.addMenu("View")
        
        self.graph_menu = self.bar.addMenu("Box")
        self.add_random_points_action = QtGui.QAction("Add random points", self)
        self.add_random_points_action.triggered.connect(self.add_random_points)
        self.graph_menu.addAction(self.add_random_points_action)
        self.batch_run_action = QtGui.QAction("Batch run", self)
        self.batch_run_action.triggered.connect(self.batch_run)
        self.graph_menu.addAction(self.batch_run_action)
        
        # optional feature
        self.initialize_matplotlib_menu()
        
    # should be INHERITED by gl_plot
    def initialize_matplotlib_menu(self):
        try: # entire optional functionality
            from ..mplot import show_ax, show_ax3d, show_fig
            from ..mplot import maxes
            from ..mplot import maxes3d
            from ..mplot import mfigs
            from ..mplot import misc as mmisc
            self.matplotlib_menu = self.bar.addMenu("Matplotlib")
            self.matplotlib_actions = []
            
            self.matplotlib_2D_menu = self.matplotlib_menu.addMenu("2D plots")
            self._setup_mpl_submenu(self.matplotlib_2D_menu, maxes, show_ax)
            
            self.matplotlib_3D_menu = self.matplotlib_menu.addMenu("3D plots")
            self._setup_mpl_submenu(self.matplotlib_3D_menu, maxes3d, show_ax3d)
                
            self.matplotlib_figs_menu = self.matplotlib_menu.addMenu("Complex plots")
            self._setup_mpl_submenu(self.matplotlib_figs_menu, mfigs, show_fig)
            
            self.matplotlib_misc_menu = self.matplotlib_menu.addMenu("Others")
            for smthng in mmisc.__all__:
                mpl_action = QtGui.QAction(smthng, self)
                show_mpl = self._mpl_prepare_fn(getattr(mmisc, smthng))
                mpl_action.triggered.connect(show_mpl)
                self.matplotlib_misc_menu.addAction(mpl_action)
                # prevent GC from wiping out both Qt object and our function
                self.matplotlib_actions.append((mpl_action, show_mpl))
                
        except ImportError as e:
            msg = "Matplotlib related features are unavailiable"
            print(self.__class__.__name__ + ":", msg, repr(e))
    
    def _setup_mpl_submenu(self, menu, module, handler):
        for drawing in module.__all__:
            mpl_action = QtGui.QAction(drawing, self)
            # do not really understand what I am doing :(
            # try to show_mpl remember its actual drawing string
            show_mpl = self._mpl_prepare_show_fn(handler, getattr(module, drawing))
            mpl_action.triggered.connect(show_mpl)
            menu.addAction(mpl_action)
            # prevent GC from wiping out both Qt object and our function
            self.matplotlib_actions.append((mpl_action, show_mpl))
    
    def _mpl_prepare_show_fn(self, show, to_draw):
        return lambda: show(to_draw, self.get_sample_box(), space=self.space)
        
    def _mpl_prepare_fn(self, fn):
        return lambda: fn(sample_box=self.get_sample_box(), space=self.space)
        
    def get_sample_box(self):
        nsim = self.slider.value()
        if nsim == self.sample_box.nsim:
            return self.sample_box
        else:
            return self.sample_box[:nsim]
    
    # INHERITED by gl_plot
    # intended as a common setup function
    def setup(self):    
        
        
        self.setup_menubar()
        
        ### Create some widgets to be placed inside
        self.combo_space = pg.ComboBox(items=self.spaces, default=self.space)
        self.combo_space.activated[str].connect(self.change_space)
        
        
        self.label_nsim = QtGui.QLabel()
        self.label_nsim.setText(str(self.sample_box.nsim))
        
        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        self.slider.valueChanged.connect(self._slider_chainged)
        self.slider.sliderReleased.connect(lambda:self.slice_changed.emit())
        self.slider.setMaximum(self.sample_box.nsim)
        self.slider.setValue(self.sample_box.nsim)
        
        #č jen aby se slider probral, když uživatel ručně přídá bodíky
        self.redraw_called.connect(self.wake_up_slider)
        self.box_runned.connect(self.wake_up_slider)
        
        self.btn = QtGui.QPushButton('run box')
        self.btn.clicked.connect(self.run_sb)
        
        self.btn2 = QtGui.QPushButton('connect/disconnect')
        self.btn2.setCheckable(True)
        self.btn2.clicked.connect(self.bx_connect)
        
        self.btn3 = QtGui.QPushButton('redraw')
        self.btn3.clicked.connect(lambda:self.redraw_called.emit())
        
        
        ## Create a grid layout to manage the widgets size and position
        self.layout = pg.LayoutWidget()
        self.setCentralWidget(self.layout)
        #self.w.setLayout(self.layout)

        # 
        self.list_view = QtGui.QListWidget()
        
        ## Add widgets to the layout in their proper positions
        #self.layout.addWidget(self.list_view, 0, 0, 2, 1) 
        self.layout.addWidget(self.combo_space, 0, 0)   
        self.layout.addWidget(self.slider, 0, 1)   
        self.layout.addWidget(self.label_nsim, 0, 2)
        self.layout.addWidget(self.btn, 0, 3) 
        self.layout.addWidget(self.btn2, 0, 4)
        self.layout.addWidget(self.btn3, 0, 5) 
        self.layout.addWidget(self.central_widget, 1, 0, 1, 6)
        


        # status bar, mainly to observe BlackBox
        self.statusBar = QtGui.QStatusBar()
        self.setStatusBar(self.statusBar)
        self.btn_continue = QtGui.QPushButton('continue')
        self.continue_label = QtGui.QLabel()
#        self.continue_layout = QtGui.QHBoxLayout()
#        self.continue_layout.addWidget(self.btn_continue)
#        self.continue_layout.addWidget(self.continue_label)
        self.statusBar.addWidget(self.btn_continue)
        self.statusBar.addWidget(self.continue_label)
        self.btn_continue.hide()
        self.continue_label.hide()
        
        self.statusBar.showMessage("Vitáme vás u nás!")


        
        
        # Dockables, najzajimavejší věc
        self.dockables = []
        
        
        dock = QtGui.QDockWidget("Interactive python console", self)
        dock.setWidget(console.ConsoleWidget(namespace={**locals(), **globals()}))
        self.dockables.append(dock)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, dock)
        

        dock = QtGui.QDockWidget("BlackBox output", self)
        self.output_label = QtGui.QLabel()
        dock.setWidget(self.output_label)
        self.dockables.append(dock)
        self.tabifyDockWidget(self.dockables[0], dock)
        
        self.simplex_data = qt_graph_widgets.SimplexEstimationData(self.sample_box, self)
        #č graphy už nemusí jít po stm widgetech
        dock = QtGui.QDockWidget("TRI_overall estimation graph", self)
        dock.setWidget(qt_graph_widgets.TriEstimationGraph(self.sample_box, 'TRI_overall_estimations', self, dock))
        self.dockables.append(dock)
        self.tabifyDockWidget(self.dockables[0], dock)
        
        dock = QtGui.QDockWidget("Simplex estimation graph", self)
        dock.setWidget(qt_graph_widgets.SimpleSimplexEstimationGraph(self.sample_box, self, dock))
        self.dockables.append(dock)
        self.tabifyDockWidget(self.dockables[0], dock)
        
        dock = QtGui.QDockWidget("Simplex error graph", self)
        dock.setWidget(qt_graph_widgets.SimplexErrorGraph(self.simplex_data, dock))
        self.dockables.append(dock)
        self.tabifyDockWidget(self.dockables[0], dock)
        
        
        dock = QtGui.QDockWidget("Voronoi estimation graph", self)
        dock.setWidget(qt_graph_widgets.VoronoiEstimationGraph(self.sample_box, self, dock))
        self.dockables.append(dock)
        self.tabifyDockWidget(self.dockables[0], dock)




        dock = dock_l = QtGui.QDockWidget("Box tree", self)
        dock.setWidget(BoxTreeWidget(self, dock))
        self.dockables.append(dock)
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, dock)
        
        
        
        dock = QtGui.QDockWidget("View", self)
        dock.setWidget(self.list_view)
        self.dockables.append(dock)
        self.tabifyDockWidget(dock_l, dock)
        
        
        
        
        for dock in self.dockables:
            self.view.addAction(dock.toggleViewAction())
            #dock.setFloating(True)
      
        #self.w.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.items)
       
       
       
       
    csv_filter = "CSV files (*.csv)"
    npy_filter = "NumPy binary files (*.npy)"
    txt_filter = "Text files (*.txt)"
    gz_filter = "Compressed text files (*.gz)"
       
    def load_coordinates(self):
        """
        Loads numpy data and adds them to the sample_box
        """ 
        nodes = None
        filter = self.npy_filter + ';;' + self.txt_filter + ';;' + self.gz_filter
        wt = self.sample_box
        
        try:
            filename, filter = pg.FileDialog.getOpenFileName(
                    parent=self, 
                    caption="Load coordinates",
                    directory="store/%s_%sD.npy" % (wt.gm_signature, wt.nvar),
                    filter=filter, 
                    initialFilter=self.npy_filter,
                    #options=pg.FileDialog.Option.DontConfirmOverwrite
                    )
            if filename:
                if filter == self.npy_filter:
                    raw_nodes = np.load(filename) 
                else:
                    raw_nodes = np.loadtxt(filename) 
                
                # create sample
                nodes = wt.f_model.new_sample(sample=raw_nodes, \
                                    space=self.space, extend=False)
                
        except BaseException as e:
            print(self.__class__.__name__ + ":", \
                            " coordinates loading failed", repr(e))
                            
        #č zde jíž nemá cenu chytat chyby. Sbohem!
        if nodes is not None:
            # run!
            wt.run_sample(nodes)
            self.box_runned.emit()
        
        
    def export_coordinates(self):
        """
        Exports current sample box coordinates
        """ 
        filter = self.npy_filter + ';;' + self.txt_filter + ';;' + self.gz_filter
        wt = self.sample_box
        
        filename, filter = pg.FileDialog.getSaveFileName(
                parent=self, 
                caption="Export coordinates",
                directory="store/%s_%sD.npy" % (wt.gm_signature, wt.nvar),
                #filter=csv_filter,
                filter=filter, 
                initialFilter=self.npy_filter,
                #options=pg.FileDialog.Option.DontConfirmOverwrite
                )
        if filename:
            nsim = self.slider.value()
            nodes = self.sample_box.f_model[:nsim]
            raw_nodes = getattr(nodes, self.space)
            try:
                if filter == self.npy_filter:
                    np.save(filename, raw_nodes, allow_pickle=False)
                else:
                    np.savetxt(filename, raw_nodes) 
            except BaseException as e:
                print(self.__class__.__name__ + ":", \
                            " coordinates export failed", repr(e)) 
        
        
        
    def import_samples(self):
        wt = self.sample_box
        
        filename, __filter = pg.FileDialog.getOpenFileName(
                parent=self, 
                caption="Import samples",
                directory="store/%s_%sD.csv" % (wt.gm_signature, wt.nvar),
                #filter=csv_filter,
                filter=self.csv_filter, # + ";;All files(*)", 
                initialFilter=self.csv_filter,
                #options=pg.FileDialog.Option.DontConfirmOverwrite
                )
        if filename and (filename[-4:]=='.csv'):
            #č Reader vždy dopíše '.csv' na konci souboru
            #č nechcu to teď měnit
            filename = filename[:-4]
            
            #č nechcu chytat žádné chyby. Sbohem!
            sample_box = reader.reader(filename, f_model=wt.f_model)
            wt.add_sample(sample_box)
            self.box_runned.emit()

    def export_samples(self):
        wt = self.sample_box
        
        filename, __filter = pg.FileDialog.getSaveFileName(
                parent=self, 
                caption="Export samples",
                directory="store/%s_%sD.csv" % (wt.gm_signature, wt.nvar),
                #filter=csv_filter,
                filter=self.csv_filter, # + ";;All files(*)", 
                initialFilter=self.csv_filter,
                #options=pg.FileDialog.Option.DontConfirmOverwrite
                )
        if filename:
            #č Reader vždy dopíše '.csv' na konci souboru
            #č nechcu to teď měnit
            if filename[-4:]=='.csv':
                filename = filename[:-4]
            nsim = self.slider.value()
            reader.export(filename, wt[:nsim])


    #
    #č Tlačítka!
    #

    # INHERITED by gl_plot
    def _slider_chainged(self, value):
        #č .setMaximum() nezpůsobuje emitování slice_changed, jsem zkontroloval
        self.slider.setMaximum(self.sample_box.nsim)
        self.label_nsim.setText(str(value))
        if not self.slider.isSliderDown(): # co to vůbec děla?
            self.slice_changed.emit()
            
    # INHERITED by gl_plot
    def change_space(self, space):
        self.space = space
        self.space_changed.emit()
        #self.plot_widget_2d()
        #self.slice_plot_data()
        
    # INHERITED by gl_plot
    def run_sb(self):
        with pg.BusyCursor():
            self.last_shot = self.sample_box()
            self.box_runned.emit()

    def wake_up_slider(self):
        # slider
        #č zpusobí slice_changed
        self.slider.setMaximum(self.sample_box.nsim)
        self.slider.setValue(self.sample_box.nsim)
        
    # INHERITED by gl_plot
    def bx_connect(self):
        if self.btn2.isChecked():
            try:
                self.sample_box.connect(self.logger)
            except BaseException as e:
                print(self.__class__.__name__ + ":", "connection to BlackBox failed", repr(e))
        else:
            try:
                self.sample_box.disconnect()
            except BaseException as e:
                print(self.__class__.__name__ + ":", "error while disconnecting of BlackBox", repr(e))
        
    # INHERITED by gl_plot
    def logger(self, *args, msg="", indent=0, **kwargs):
        self.continue_label.setText("BlackBox: " + msg)
        self.output_label.setText(str(args) + str(kwargs))
        
        loop = QtCore.QEventLoop()
        self.btn_continue.clicked.connect(loop.quit)
        self.btn_continue.show()
        self.continue_label.show()
        # i want to clear status bar temporaly
        status = self.statusBar.currentMessage()
        self.statusBar.clearMessage()
        
        loop.exec_()  # Execution stops here until finished called  
        
        self.btn_continue.hide()
        self.continue_label.hide()
        self.statusBar.showMessage(status)
        
    def add_random_points(self):
        ns, ok = QtGui.QInputDialog.getInt(
        			self,"Add random points","number", value=1, min=1)
        if ok:
            wt = self.sample_box
            nodes = wt.f_model(ns)
            wt.run_sample(nodes)
            self.box_runned.emit()
        
    def batch_run(self):
        #pg.QtGui.QInputDialog.getInt(
        runs, ok = QtGui.QInputDialog.getInt(
        			self,"Batch run","runs", value=100, min=1)
  
        if ok:
            with pg.ProgressDialog("Running..", 0, runs,\
            			 cancelText='Stop', busyCursor=True) as dlg:
                for i in range(runs):
                    # keep the GUI responsive :)
                    self.app.processEvents()
                    
                    self.last_shot = self.sample_box()
                
                    # slider
                    #č zpusobí slice_changed
                    self.slider.setMaximum(self.sample_box.nsim)
                    self.slider.setValue(self.sample_box.nsim)
                    
                    self.box_runned.emit()
                    
                    dlg += 1
                    if dlg.wasCanceled():
                        break
                    
         
    


        






"""
===========
♥ Чыры-пыры 
č Jiné
E Miscellaneous
===============
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"""



class BoxTreeWidget(pg.LayoutWidget):
    """
    """
    # I'd like to get access to the samplebox stuff via the container's reference, 
    # but relying to Qt's parent mechanism makes me worry.
    def __init__(self, samplebox_item,  parent=None, *args, **kwargs):
        super().__init__(parent)
        # sb like samplebox, of course
        self.sb_item = samplebox_item
        
        
        self.btn = QtGui.QPushButton('update')
        self.addWidget(self.btn, row=0, col=0)
        self.btn.clicked.connect(self.update)
        
        #self.tree = pg.DataTreeWidget(self, data=self.get_data(samplebox_item))
        self.tree = pg.DataTreeWidget(self, data=dict())
        self.addWidget(self.tree, row=1, col=0)

    def update(self, *args, **kwargs):
        try:
            self.tree.setData(self.get_data(self.sb_item), hideRoot=True)
        except BaseException as e:
            msg = ""
            error_msg = self.__class__.__name__ + ": " + msg + repr(e)
            print(error_msg)
            
    @staticmethod
    def get_data(self): #č nenechej si splest tím "self", je to prostě reference na QtGuiPlot2D
        data_tree = dict()
        data_tree['self.sample_box'] = self.sample_box.__dict__
        try: # shapeshare
            data_tree['self.sample_box.shapeshare'] = self.sample_box.shapeshare.__dict__
        except AttributeError:
            pass
        try:
            data_tree['self.sample_box.dicebox'] = self.sample_box.dicebox.__dict__
        except AttributeError:
            pass
        try:
            data_tree['self.sample_box.reader'] = self.sample_box.reader.__dict__
        except AttributeError:
            pass
        try:
            data_tree['self.sample_box.samplebox'] = self.sample_box.samplebox.__dict__
        except AttributeError:
            pass
        try:
            data_tree['self.sample_box.candybox'] = self.sample_box.candybox.__dict__
        except AttributeError:
            pass
        try:
            data_tree['self.sample_box.f_model'] = self.sample_box.f_model.__dict__
        except AttributeError:
            pass
            
        return data_tree


        


Mode Type Size Ref File
100644 blob 28117 0907e38499eeca10471c7d104d4b4db30b8b7084 IS_stat.py
100644 blob 6 0916b75b752887809bac2330f3de246c42c245cd __init__.py
100644 blob 72 458b7e2ca46acd9ec0d2caf3cc4d72e515bb73dc __main__.py
100644 blob 73368 3d245b8568158ac63c80fa0847631776a140db0f blackbox.py
100644 blob 11243 10c424c2ce5e8cdd0da97a5aba74c54d1ca71e0d candybox.py
100644 blob 29927 066a2d10ea1d21daa6feb79fa067e87941299ec4 convex_hull.py
100644 blob 102798 059ae717e71c651975673420cd8230fbef171e5e dicebox.py
100644 blob 36930 a775d1114bc205bbd1da0a10879297283cca0d4c estimation.py
100644 blob 34394 3f0ab9294a9352a071de18553aa687c2a9e6917a f_models.py
100644 blob 35721 3daee87ec0bc670207356490e16f200fed0d4fc4 g_models.py
100644 blob 20908 457329fe567f1c0a9950c21c7c494cccf38193cc ghull.py
100644 blob 2718 5d721d117448dbb96c554ea8f0e4651ffe9ac457 gp_plot.py
100644 blob 29393 96162a5d181b8307507ba2f44bafe984aa939163 lukiskon.py
100644 blob 2004 6ea8dc8f50a656c48f786d5a00bd6398276c9741 misc.py
040000 tree - c1998680b852b2d9d44417719a05dba630503368 mplot
100644 blob 1462 437b0d372b6544c74fea0d2c480bb9fd218e1854 plot.py
100644 blob 2807 1feb1d43e90e027f35bbd0a6730ab18501cef63a plotly_plot.py
040000 tree - bfb2adfd17a5c916d2a132e2607f57f14561559e qt_gui
100644 blob 8566 5c8f8cc2a34798a0f25cb9bf50b5da8e86becf64 reader.py
100644 blob 4284 a0e0b4e593204ff6254f23a67652804db07800a6 samplebox.py
100644 blob 6558 df0e88ea13c95cd1463a8ba1391e27766b95c3a5 sball.py
100644 blob 6739 0b6f1878277910356c460674c04d35abd80acf13 schemes.py
100644 blob 76 11b2fde4aa744a1bc9fa1b419bdfd29a25c4d3e8 shapeshare.py
100644 blob 54884 fbe116dab4fc19bb7568102de21f53f15a8fc6bf simplex.py
100644 blob 13090 2b9681eed730ecfadc6c61b234d2fb19db95d87d spring.py
100644 blob 10953 da8a8aaa8cac328ec0d1320e83cb802b562864e2 stm_df.py
040000 tree - 8528f771e081cc1d2a9dc50a6e1d4f8f20b711d6 testcases
100644 blob 2465 d829bff1dd721bdb8bbbed9a53db73efac471dac welford.py
100644 blob 25318 fcdabd880bf7199783cdb9c0c0ec88c9813a5b18 whitebox.py
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/iam-git/WellMet

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/iam-git/WellMet

Clone this repository using git:
git clone git://git.rocketgit.com/user/iam-git/WellMet

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main