Friday, December 13, 2024

My own Mind Map program in Java script and Python

I had been searching online mindmap apps for my study for a while and never got one that I am really happy with. 

Then I asked myself what I really need for a mind map to do? My requirements are pretty simple:

It allows me to put in text and create child nodes. It can be saved and re-opened for later editing. I should be able to print it as a picture/pdf.

With some help from ChatGPT, I was actually able to create one that works very well so I am sharing it with all of you here.




Full source code in Python.


import tkinter as tk

from tkinter import filedialog

import json



class MindMapApp:

    def __init__(self, root):

        self.root = root

        self.root.title("Mind Map Tool")

        self.root.geometry("800x600")


        # Main canvas

        self.canvas = tk.Canvas(root, width=780, height=500, bg="lightgray")

        self.canvas.pack(pady=10)


        # Buttons

        button_frame = tk.Frame(root)

        button_frame.pack()

        tk.Button(button_frame, text="Add Text Box", command=lambda: self.add_text_box()).pack(side=tk.LEFT, padx=5)

        tk.Button(button_frame, text="Save", command=self.save_mind_map).pack(side=tk.LEFT, padx=5)

        tk.Button(button_frame, text="Open", command=self.open_mind_map).pack(side=tk.LEFT, padx=5)


        self.text_boxes = []  # List to track all text boxes

        self.connections = []  # List to track parent-child relationships for drawing lines


    def add_text_box(self, parent=None, x=None, y=None, text=""):

        """Add a new text box. If a parent is provided, connect the sub-note to it."""

        if x is None or y is None:

            # Add a top-level text box with default placement

            x, y = 50 + len(self.text_boxes) * 30, 50 + len(self.text_boxes) * 30


        # Create the text box and "+" button

        new_text_box = tk.Text(self.root, width=20, height=2, wrap="word")

        new_text_box.place(x=x, y=y)

        new_text_box.insert("1.0", text)

        new_text_box.bind("<KeyRelease>", lambda e: self.adjust_text_box_size(new_text_box))

        new_text_box.focus_set()  # Automatically set focus to the new text box


        add_button = tk.Button(self.root, text="+", command=lambda: self.add_text_box(new_text_box))

        add_button.place(x=x - 30, y=y)


        # Link the "+" button to the text box

        new_text_box._add_button = add_button


        self.text_boxes.append(new_text_box)


        # Add dragging functionality

        self.make_draggable(new_text_box)


        # Add right-click menu for deletion

        self.add_context_menu(new_text_box)


        # Draw a curved, dotted line connecting parent and child

        if parent is not None:

            # Determine non-overlapping position

            x, y = self.find_non_overlapping_position(parent)

            line_id = self.canvas.create_line(

                parent.winfo_x() + 90, parent.winfo_y() + 10,  # Parent's right center

                x, y + 10,  # Child's left center

                fill="black",

                dash=(4, 2),

                smooth=True,

            )

            self.connections.append((parent, new_text_box, line_id))


    def find_non_overlapping_position(self, parent):

        """Find a non-overlapping position for a new child text box."""

        x = parent.winfo_x() + 150

        y = parent.winfo_y()

        while any(

            abs(x - t.winfo_x()) < 100 and abs(y - t.winfo_y()) < 50

            for t in self.text_boxes

        ):

            y += 50  # Move down until a free space is found

        return x, y


    def adjust_text_box_size(self, text_box):

        """Adjust the size of the text box based on its content."""

        content = text_box.get("1.0", "end-1c")

        lines = content.split("\n")

        width = max(len(line) for line in lines)

        height = len(lines)

        text_box.config(width=max(20, width), height=max(2, height))


    def make_draggable(self, widget):

        """Make a widget draggable."""

        def start_drag(event):

            widget._drag_start_x = event.x

            widget._drag_start_y = event.y


        def drag(event):

            # Calculate new position

            new_x = widget.winfo_x() + event.x - widget._drag_start_x

            new_y = widget.winfo_y() + event.y - widget._drag_start_y

            widget.place(x=new_x, y=new_y)


            # Update "+" button position

            if hasattr(widget, "_add_button"):

                widget._add_button.place(x=new_x - 30, y=new_y)


            # Redraw all lines

            self.redraw_lines()


        widget.bind("<Button-1>", start_drag)

        widget.bind("<B1-Motion>", drag)


    def add_context_menu(self, widget):

        """Add a right-click context menu to delete the widget."""

        menu = tk.Menu(self.root, tearoff=0)

        menu.add_command(label="Delete", command=lambda: self.delete_text_box(widget))


        def show_context_menu(event):

            menu.tk_popup(event.x_root, event.y_root)


        widget.bind("<Button-3>", show_context_menu)


    def delete_text_box(self, widget):

        """Delete a text box, its "+" button, and associated lines."""

        # Remove associated "+" button

        if hasattr(widget, "_add_button"):

            widget._add_button.destroy()


        # Remove connections and lines related to the widget

        for connection in self.connections[:]:

            parent, child, line_id = connection

            if parent == widget or child == widget:

                self.canvas.delete(line_id)

                self.connections.remove(connection)


        # Destroy the widget

        widget.destroy()


        # Remove from text_boxes list

        self.text_boxes.remove(widget)


    def redraw_lines(self):

        """Redraw all lines to keep connections intact when widgets are moved."""

        for parent, child, line_id in self.connections:

            # Update line coordinates based on the current positions

            self.canvas.coords(

                line_id,

                parent.winfo_x() + 90, parent.winfo_y() + 10,  # Parent's right center

                child.winfo_x(), child.winfo_y() + 10,  # Child's left center

            )


    def save_mind_map(self):

        """Save the current mind map to a JSON file."""

        mind_map = []

        for text_box in self.text_boxes:

            mind_map.append({

                "x": text_box.winfo_x(),

                "y": text_box.winfo_y(),

                "text": text_box.get("1.0", "end-1c"),

            })


        connections = []

        for parent, child, _ in self.connections:

            connections.append({

                "parent_index": self.text_boxes.index(parent),

                "child_index": self.text_boxes.index(child),

            })


        data = {"text_boxes": mind_map, "connections": connections}


        file_path = filedialog.asksaveasfilename(defaultextension=".json", filetypes=[("JSON files", "*.json")])

        if file_path:

            with open(file_path, "w") as file:

                json.dump(data, file)


    def open_mind_map(self):

        """Open a mind map from a JSON file."""

        file_path = filedialog.askopenfilename(filetypes=[("JSON files", "*.json")])

        if file_path:

            with open(file_path, "r") as file:

                data = json.load(file)


            # Clear current mind map

            for text_box in self.text_boxes[:]:

                self.delete_text_box(text_box)


            # Add text boxes

            for item in data["text_boxes"]:

                self.add_text_box(x=item["x"], y=item["y"], text=item["text"])


            # Add connections

            for conn in data["connections"]:

                parent = self.text_boxes[conn["parent_index"]]

                child = self.text_boxes[conn["child_index"]]

                line_id = self.canvas.create_line(

                    parent.winfo_x() + 90, parent.winfo_y() + 10,

                    child.winfo_x(), child.winfo_y() + 10,

                    fill="black", dash=(4, 2), smooth=True

                )

                self.connections.append((parent, child, line_id))



# Run the application

root = tk.Tk()

app = MindMapApp(root)


# Periodically redraw lines to ensure they're updated

def periodic_redraw():

    app.redraw_lines()

    root.after(100, periodic_redraw)



periodic_redraw()

root.mainloop()



Sunday, April 14, 2024

腓立比書 4:6-7

 《經文:腓立比書 4:6-7》

6應當一無罣慮,只要凡事藉著禱告、祈求和感謝,將你們所要的告訴神。7神所賜出人意外的平安,必在基督耶穌裡,保守你們的心懷意念。

Friday, March 10, 2023

Question 7, 8, 9, 10

Which two are true about the Automatic Database Diagnostic Monitor (ADDM)? (Choose two.)

A. It analyzes a period of time corresponding to the 12 hours of activity.

B. It runs automatically after each AWR snapshot.

C. A DBA can run it manually.

D. Results are written to the alert log.

E. It analyzes a period of time corresponding to the last day of activity.

Correct Answer: BC

A: ADDM analyzes the data collected between two AWR snapshots, which is typically a one-hour period. So 12 hour period is wrong.

B: ADDM runs automatically after each AWR snapshot, which is typically every hour by default.

C: The DBA can also manually run ADDM by calling the sql scripts under rdbms/admin directory. 

D: ADDM is not written to alert log. The results are saved in the database.

E: Not to the last day of activity.


Which two are true about server-generated alerts? (Choose two.)

A. Stateful alerts must be cleared by a DBA after resolving the problem.

B. Stateless alerts can be purged manually from the alert history.

C. Stateless alerts can be cleared manually.

D. Stateless alerts are automatically cleared.

E. Stateful alerts are purged automatically from the alert history.

Correct Answer: BC

The database considers all the non-threshold alerts as stateless alerts. A stateful alert first appears in the DBA_OUTSTANDING_ALERTS view and goes to the DBA_ALERT_HISTORY view when it is cleared. A stateless alert goes straight to DBA_ALERT_HISTORY.(http://www.remote-dba.net)

For metric alert event types, an event (metric alert) is raised based on the metric threshold values. These metric alert events are called stateful alerts. For those metric alert events that are not tied to the state of a monitored system (for example, snapshot too old, or resumable session suspended ), these alerts are called stateless alerts. Because stateless alerts are not cleared automatically, they need to be cleared manually.

Which three are located by using environment variables? (Choose three.)

A. the Optimal Flexible Architecture (OFA) compliant path to store Oracle software and configuration files.

B. the location of Oracle Net Services configuration files

C. the list of a disk group names to be mounted by an Oracle Automatic Storage Management (ASM) instance at startup

D. default directories for temporary files used by temporary tablespaces

E. the temporary disk space used by Oracle Installer during installation

F. the maximum number of database files that can be opened by a database instance

Correct Answer:  ABE

A: ORACLE_BASE, ORACLE_HOME 
B: TNS_ADMIN
C: Not env variables but in ASM pfile/spfile.
D: Not env variables but by controlfile.
E: TMP, TEMP
F: Not env variables but in database pfile/spfile.

Which three are true about opatchauto? (Choose three.)

A. It performs a shutdown and then a restart of all processes in both Oracle Grid Infrastructure and Oracle Database home during the patching process.

B. It must be invoked by a user with root user privileges.

C. Patches are applied via opatchauto.

D. Users must always input patch plans to opatchauto.

E. It requires the Oracle Grid Infrastructure and Oracle Database instances to be shut down before being invoked.

F. It applies patches in nonrolling mode by default.

G. It is used to apply interim patches to Oracle Grid Infrastructure and Oracle Database home combinations.

Correct Answer: BCE  BCG

A. Totally wrong statement.

B. Yes you have to use root.

C. True.

D. Patch plan is optional.

E. opatchauto performs shutdown and restart so this is false.

F. It can do rolling patch for RAC systems.

G.True. It patches both GRID and RDBMS homes.

Wednesday, March 8, 2023

Question 5, 6

  • Which three actions are performed by the Oracle Preinstallation RPM, oracle-database-server-xxxx-preinstall, for Oracle Grid Infrastructure, where xxxx is the Oracle version and release? (Choose three.)

A. performing checks to ensure minimum configuration requirements for Oracle Grid Infrastructure are met

B. creating the oracle OS user

C. creating the OSDBA (dba) group

D. creating the oraInventory (oinstall) group

E. creating the grid OS user

F. configuring the OS for Oracle Automatic Storage Management shared storage access

 

Correct Answer: BCD

Not much are needed to explain for this question. Here is a link with related information.

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/ladbi/about-the-oracle-preinstallation-rpm.html#GUID-C15A642B-534D-4E4A-BDE8-6DC7772AA9C8


  • Which two are true about common objects? (Choose two.)

A. They can be created only in CDB$ROOT.

B. They can be only metadata-linked in an application container.

C. They can exist in user-defined schemas only in application containers.

D. They can exist in CDB$ROOT and an application root.

E. They can be extended data-linked in CDB$ROOT.

F. They can be created only in an application root.

 

Correct Answer: DE





1z0-083

 my 1z0-083 posts:

https://www.linkedin.com/posts/kenchendz_multitenant-usertablespaces-clause-in-activity-7039298088187961344-chDS?utm_source=share&utm_medium=member_desktop

https://www.linkedin.com/posts/kenchendz_multitenant-usertablespaces-clause-in-activity-7039305152511545344-Q-Yq?utm_source=share&utm_medium=member_desktop


More to come...

A mystery issue

A mystery issue just got resolved.

A team complained to me that one of their jobs sometimes failed and sometimes worked well. The job calls a database stored procedure to write a CSV file to a directory on the database server.

So they engaged both the Unix admin and me, the DBA. I had them run the job manually from the server locally using SQLplus, and it worked. Unix admin checked the privileges on the directory and it was 777. 

So I am very sure nothing is wrong on the database server side. Even they insisted that the only thing that changed was that we patched the database server last week.

At first, I thought it was a network blip. But the network team stated there were no issues or maintenance.

Then, I asked the app team to provide the tnsnames.ora file. The TNS entry explains the issue. There were two entries in one that was used to connect to the database. One is pointing to the DR and the other is pointing to Primary. When the primary is used, it works. When the DR is picked, it fails.

The app team said they just copied and pasted so they didn't check.

Sigh...

Thursday, July 23, 2020

12c OCM note 02 -- database Vs instance / CDB architect / connect to CDB, PDBs/ Tools

database Vs instance
=================
11g:
single instance: one database -- one instance
RAC: one database -- two or more instances

12c:
non-CDB: same as 11g
CDB:
single instance: one or more databases -- one instance
RAC: one or more databases -- two or more instances




CDB architect
=================
One instance is shared by multiple databases(cdb$root, pdb$seed, other pdbs)


Connect to CDB, PDBs
=================

Connect to root: sqlplus / as sysdba conn / as sysdba
EZ connect: sqlplus sys/oracle@localhost/PRODCDB.example.com:1521 as sysdba sqlplus sys/oracle@host02/PRODCDB.example.com:1521 as sysdba sqlplus sys/oracle@192.168.59.59/PRODCDB.example.com:1521 as sysdba
tns: sqlplus sys/oracle@tns_prodcdb as sysdba
switch container
alter session set container=cdb$root; Connect to PDB: EZ connect: sqlplus sys/oracle@localhost/pdbprod1.example.com:1521 as sysdba sqlplus sys/oracle@192.168.59.59/pdbprod1.example.com:1521 as sysdba tns: sqlplus sys/oracle@tns_pdb1 as sysdba switch container: alter session set container=PDBPROD2; ---Some useful views col name format a20 col pdb format a10 select name,pdb from cdb_services order by con_id; select name,pdb from v$services order by con_id;

Tools
=================
SQL developer
OEM
TOAD --- Not recommended.

My own Mind Map program in Java script and Python

I had been searching online mindmap apps for my study for a while and never got one that I am really happy with.  Then I asked myself what I...