[Java Swing] Komunikacja między wątkami i komponentami Swing

[Java Swing] Komunikacja między wątkami i komponentami Swing
sprzedamsanki
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 8 lat
  • Postów:278
0

Witam,

Mam za zadanie stworzyć prosty interfejs użytkownika, w którym naciśnięcie przycisku uruchamia kilka wątków, które w wypisują odpowiednie treści w polu tekstowym. Jak się tworzy komunikację między wątkami (czy szerzej, klasami spoza klasy implementującej okno), a komponentami okna?


The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' (I found it!) but 'That's funny'
Black007
  • Rejestracja:ponad 21 lat
  • Ostatnio:dzień
0

Cześć.

Watek jest obiektem klasy rozszerzającej Thread, lub implementującej interfejs Runnable. (ja używam tego drugiego sposobu).

Ponieważ jest to "zwykła" klasa, to możesz do niej przekazać obiekt pola tekstowego, lub całej formatki przy pomocy konstruktora, lub settera.
Np.

Masz obiekt okno, który ma w sobie pole.
Tworzysz obiekt Wątek, implementujący Runnable.
Wywolujesz Watek.setPole(pole);
A potem:
Thread starter = new Thread(Wątek);
starter.start();

W metodzie Wątek.run() robisz pole.setText("hello world");

i tyle.

Pozdrawiam.


"Nie popełnia błędów tylko ten, kto nic nie robi"
0

Hej

Jeżeli zamierzasz z wątków odwoływać się np do tego samego komponentu, warto utworzyć metodę oznaczoną słowem synchronized np.

Kopiuj
class ObslugaPrzyciskow implements Runnable
{
private JTextField field;
public ObsługaPrzyciskow(JTextField jt)
{
field = jt;
}

public void run() //implementacja metody z interfejsu Runnable, chyba tak się nazywa
{
for(int i=0 ; i<1000 ; i++)
{
update();
try{ Thread.sleep(1000);} catch (Exception ex){}
}
}

private synchronized void update()
{
 field.setText(field.getText() +" " + "jakiś nowy tekst");
}

}
 

Napisałem to z głowy to mogłem gdzieś się pomylić, ale mniej więcej taka koncepcja.

Potem tworzysz

Kopiuj
Thread t = new Thread(new ObsługaPrzyciskow());
t.start();
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:34 minuty
0

Jeśli chodzi o Swinga to nie należy używać zwykłych wątków tylko ładować SwingWorkery: http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html . GUI powinno aktualizować tylko z poziomu EDT, inaczej mogą powstawać trudne do zlokalizowania błędy. Swing nie jest thread-safe, nie udało się zrobić swego czasu wielowątkowego Swinga i jak na razie nic mi nie wiadomo, żeby miało się to zmienić.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
sprzedamsanki
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 8 lat
  • Postów:278
0
donkey7 napisał(a)

Jeśli chodzi o Swinga to nie należy używać zwykłych wątków tylko ładować SwingWorkery: http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html . GUI powinno aktualizować tylko z poziomu EDT, inaczej mogą powstawać trudne do zlokalizowania błędy. Swing nie jest thread-safe, nie udało się zrobić swego czasu wielowątkowego Swinga i jak na razie nic mi nie wiadomo, żeby miało się to zmienić.

oracle.com napisał(a)

SwingWorker is only designed to be executed once. Executing a SwingWorker more than once will not result in invoking the doInBackground method twice.

A istnieje jakaś wersja która by działała cały czas w tle? Ja potrzebuję stworzyć wątek który cały czas wypisuje przypisaną mu literę synchronizując się z innymi wątkami (np. żeby A i B były wyświetlane na zmianę) więc SwingWorker niespecjalnie się do tego nadaje.


The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' (I found it!) but 'That's funny'
edytowany 1x, ostatnio: sprzedamsanki
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:34 minuty
0

Ale przecież:

  1. SwingWorker może działać dowolnie długo,
  2. Możesz utworzyć i uruchomić naraz wiele SwingWorkerów,
  3. Możesz zrobić podklasę rozszerzającą SwingWorkera i wrzucić jej tam jakieś obiekty do synchronizacji, mogą to być nawet inne SwingWorkery,

To powinno wystarczyć.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
sprzedamsanki
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 8 lat
  • Postów:278
0

Hm, to powiedz mi może, co ja źle robię. Stworzyłem coś takiego:

Kopiuj
public class Mainwindow extends JFrame {
     private class LetterWriter extends SwingWorker<Object, Character> {
		Character Letter;
		
		public LetterWriter(Character let){
			Letter = let;
		}
		
		@Override
		protected Object doInBackground() throws Exception {
			while(true){
				if(Kontrolka.CheckAvalability(Letter)) {
					System.out.println(Letter.toString() + " Printing");
					publish(Letter);
					Kontrolka.notifyAll();
				} else {
					System.out.println(Letter.toString() + " Waiting...");
					Kontrolka.wait();
				}
			}
		}
		
		protected void process(List<Character> chunks){
			for(Character let : chunks){
				OutputTextArea.append(let.toString());
			}
		}
	}
}  

Wątek uruchamiam za pomocą metody execute() w funkcji obsługującej naciśnięcie przycisku. Problem polega na tym, że po wywołanu funkcji notifyAll() wątek nie powtarza już ani razu swojego przebiegu. wynikiem tego programu jest wypisanie tylko jednego A. (CheckAvalability() na razie zawsze zwraca true)

Jeżeli wywalę notifyAll(), to owszem działa ale strasznie spowalnia interfejs, nie mówiąc o tym, że nie potrafię przerwać tego wątku metodą cancel()


The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' (I found it!) but 'That's funny'
edytowany 3x, ostatnio: sprzedamsanki
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:34 minuty
0
  1. W pętli powinno być isCancelled() a nie true. Nagłe wywalanie wątków jest "deprecated". Wątek sam powinien się dowiedzieć kiedy się zatrzymać.
  2. Zrób char[] z tej List<Character>(), a potem Stringa z tego char[].

Zapodaj więcej kodu, albo w ogóle kompletny test-case.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
sprzedamsanki
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 8 lat
  • Postów:278
0

Wrzucam całą klasę Mainwindow. Dodam że poprawienie tej pętli nie pomogło. Dalej nie potrafię przerwać tego wątku. Mógłbyś tez przybliżyć dlaczego powinienem zmienić na char[]?

Kopiuj
public class Mainwindow extends JFrame {

	private class Controler {
		private Character LastCharacter;
		
		public boolean CheckAvalability (Character c){
			LastCharacter = c;
			return true;
		}
	}
	
	private class LetterWriter extends SwingWorker<Object, Character> {
		Character Letter;
		
		public LetterWriter(Character let){
			Letter = let;
		}
		
		@Override
		protected Object doInBackground() throws Exception {
			while(!this.isCancelled()){
				if(Kontrolka.CheckAvalability(Letter)) {
					System.out.println(Letter.toString() + " Printing");
					publish(Letter);
				} else {
					System.out.println(Letter.toString() + " Waiting...");
					Kontrolka.wait();
				}
			}
			return null;
		}
		
		protected void process(List<Character> chunks){
			for(Character let : chunks){
				OutputTextArea.append(let.toString());
			}
		}
	}
	
	private static final long serialVersionUID = 1L;
	private JDesktopPane jDesktopPane = null;
	private JButton StartButton = null;
	private JButton StopButton = null;
	private JTextArea OutputTextArea = null;
	
	private Controler Kontrolka = null;
	private LetterWriter ThreadA = null;
	private LetterWriter ThreadB = null;

	private JDesktopPane getJDesktopPane() {
		if (jDesktopPane == null) {
			jDesktopPane = new JDesktopPane();
			jDesktopPane.setBackground(SystemColor.control);
			jDesktopPane.add(getStartButton(), null);
			jDesktopPane.add(getStopButton(), null);
			jDesktopPane.add(getOutputTextArea(), null);
		}
		return jDesktopPane;
	}

	private JButton getStartButton() {
		if (StartButton == null) {
			StartButton = new JButton();
			StartButton.setBounds(new Rectangle(202, 47, 63, 23));
			StartButton.setText("Start");
			StartButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					ThreadA.execute();
					ThreadB.execute();
					System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed()
				}
			});
		}
		return StartButton;
	}

	private JButton getStopButton() {
		if (StopButton == null) {
			StopButton = new JButton();
			StopButton.setBounds(new Rectangle(202, 98, 62, 26));
			StopButton.setText("Stop");
			StopButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					ThreadA.cancel(true);
					ThreadB.cancel(true);
					System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed()
				}
			});
		}
		return StopButton;
	}

	private JTextArea getOutputTextArea() {
		if (OutputTextArea == null) {
			OutputTextArea = new JTextArea();
			OutputTextArea.setBounds(new Rectangle(26, 23, 154, 124));
			OutputTextArea.setLineWrap(true);
			OutputTextArea.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
			OutputTextArea.setEditable(true);
			OutputTextArea.setEnabled(false);
			OutputTextArea.setBorder(BorderFactory.createLineBorder(Color.black));
		}
		return OutputTextArea;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				Mainwindow thisClass = new Mainwindow();
				thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				thisClass.setVisible(true);
			}
		});
	}

	public Mainwindow() {
		super();
		initialize();
		ThreadA = new LetterWriter('A');
		ThreadB = new LetterWriter('B');
		Kontrolka = new Controler();
	}

	private void initialize() {
		this.setSize(301, 208);
		this.setName("Main Window");
		this.setContentPane(getJDesktopPane());
		this.setTitle("Lab 3");
	}
}  

The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' (I found it!) but 'That's funny'
edytowany 4x, ostatnio: sprzedamsanki
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:34 minuty
0
  1. Zamiana na String całości od razu ze względów wydajnościowych. Podejrzewam, że metoda append(String) jest dość kosztowna, więc lepiej wywoływać ją jak najmniej razy.

  2. Uruchomiłem ten kod u siebie (Ubuntu, OpenJDK) i raczej działa poprawnie. Przeładowałem metodę done() w SwingWorkerze i widzę że się wywołuje gdy kliknę przycisk Stop. Widzę także litery A i B na zmianę, tzn ciąg liter A, potem ciąg liter B, itd czyli tak jak powinno być.

  3. Użyj tutaj typu Void, a nie Object przy parametryzowaniu SwingWorker.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
sprzedamsanki
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 8 lat
  • Postów:278
0

Klasę Cotroler zmieniłem na taką:

Kopiuj
private class Controler {
		private Character LastCharacter;
		
		public boolean CheckAvalability (Character c){
			if (LastCharacter==c) {
				return false;
			} else {
				LastCharacter = c;
				return true;
			}
		}
	}

Chciałem zsynchronizować te dwa wątki żeby litery pojawiały się na zmianę. Niestety, pojawiają się losowo niekoniecznie w tej kolejności, co gorsza same przerywają po chwili...

Dodano:
Jak dopisałem notifyAll() po opublikowaniu litery, to wywala zawsze jedno AB, jednak nie zapętla się


The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' (I found it!) but 'That's funny'
edytowany 3x, ostatnio: sprzedamsanki
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:34 minuty
0

Dodaj sobie taką metodę do SwingWorkera:

Kopiuj
@Override
        protected void done() {

            try {
                get();
            } catch (InterruptedException ex) {
                System.out.println(ex);
            } catch (ExecutionException ex) {
                System.out.println(ex);
            }
            System.out.println("Done.");
        }

A potem zobacz co się dzieje.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
sprzedamsanki
  • Rejestracja:prawie 18 lat
  • Ostatnio:około 8 lat
  • Postów:278
0

Poprzerabiałem trochę, dodałem bloki synchronized. Wątki są już teoretycznie zsynchronizowane (w konsoli wywołują się odpowiednio), jednak do komponentu dalej litery wpadają losowo. Gdzie jeszcze powinienem to synchronizować? A może w ogóle zmienić podejście?

Kopiuj
public class Mainwindow extends JFrame {

	private class Controler {
		private Character LastCharacter;
		
		public boolean CheckAvalability (Character c){
			if (LastCharacter==c) {
				return false;
			} else {
				LastCharacter = c;
				return true;
			}
		}
	}
	
	private class LetterWriter extends SwingWorker<Void, Character> {
		Character Letter;
		
		public LetterWriter(Character let){
			Letter = let;
		}
		
		@Override
		protected Void doInBackground() throws Exception {
			while(!this.isCancelled()){
				synchronized (Kontrolka) {
					if (Kontrolka.CheckAvalability(Letter)) {
						System.out.println(Letter.toString() + " Printing");
						publish(Letter);
						try {
							Kontrolka.notifyAll();
						} catch (Exception e) {
							System.out.println(e);
						}
					} else {
						System.out.println(Letter.toString() + " Waiting...");
						try {
							Kontrolka.wait();
						} catch (Exception e) {
							System.out.println(e);
						}
					}
				}
			}
			return null;
		}
		
		protected void process(List<Character> chunks){
			synchronized (OutputTextArea) {
				for (Character let : chunks) {
					OutputTextArea.append(let.toString());
				}
			}
		}
		

		@Override
		protected void done() {
			try {
				get();
			} catch (InterruptedException ex) {
				System.out.println(ex);
			} catch (ExecutionException ex) {
				System.out.println(ex);
			}
				System.out.println("Done.");
		}
	}
	
	private static final long serialVersionUID = 1L;
	private JDesktopPane jDesktopPane = null;
	private JButton StartButton = null;
	private JButton StopButton = null;
	private JTextArea OutputTextArea = null;
	
	private Controler Kontrolka = null;
	private LetterWriter ThreadA = null;
	private LetterWriter ThreadB = null;

	private JDesktopPane getJDesktopPane() {
		if (jDesktopPane == null) {
			jDesktopPane = new JDesktopPane();
			jDesktopPane.setBackground(SystemColor.control);
			jDesktopPane.add(getStartButton(), null);
			jDesktopPane.add(getStopButton(), null);
			jDesktopPane.add(getOutputTextArea(), null);
		}
		return jDesktopPane;
	}

	private JButton getStartButton() {
		if (StartButton == null) {
			StartButton = new JButton();
			StartButton.setBounds(new Rectangle(202, 47, 63, 23));
			StartButton.setText("Start");
			StartButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					ThreadA.execute();
					ThreadB.execute();
					System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed()
				}
			});
		}
		return StartButton;
	}

	private JButton getStopButton() {
		if (StopButton == null) {
			StopButton = new JButton();
			StopButton.setBounds(new Rectangle(202, 98, 62, 26));
			StopButton.setText("Stop");
			StopButton.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					ThreadA.cancel(true);
					ThreadB.cancel(true);
					System.out.println("actionPerformed()"); // TODO Auto-generated Event stub actionPerformed()
				}
			});
		}
		return StopButton;
	}

	private JTextArea getOutputTextArea() {
		if (OutputTextArea == null) {
			OutputTextArea = new JTextArea();
			OutputTextArea.setBounds(new Rectangle(26, 23, 154, 295));
			OutputTextArea.setLineWrap(true);
			OutputTextArea.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
			OutputTextArea.setEditable(true);
			OutputTextArea.setEnabled(false);
			OutputTextArea.setBorder(BorderFactory.createLineBorder(Color.black));
		}
		return OutputTextArea;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				Mainwindow thisClass = new Mainwindow();
				thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				thisClass.setVisible(true);
			}
		});
	}

	public Mainwindow() {
		super();
		initialize();
		ThreadA = new LetterWriter('A');
		ThreadB = new LetterWriter('B');
		Kontrolka = new Controler();
	}

	private void initialize() {
		this.setSize(301, 382);
		this.setName("Main Window");
		this.setContentPane(getJDesktopPane());
		this.setTitle("Lab 3");
	}
}

The most exciting phrase to hear in science, the one that heralds new discoveries, is not 'Eureka!' (I found it!) but 'That's funny'
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:34 minuty
0

Metoda process(List<V>) jest wywoływana w EDT, więc tam nie musisz nic synchronizować. Zachowanie jest zgodne z oczekiwanym, gdyż wyniki metody publish są łączone w listę, jeżeli EDT jest zajęty i nie można wywołać process od razu.

W twoim przypadku chyba jednak dobrze by było zmienić podejście. Do głowy przyszedł mi następujący pomysł:

  1. Utworzyć normalne Thready, które generują litery.
  2. W środku tych Threadów tworzyć nowe Runnable dla każdej litery i wywoływać SwingUtilites.invokeLater() na nich.

Rozwiązanie to ma jednak pewną zasadniczą wadę. Jeżeli skolejkujesz za dużo zadań to interfejs się zawiesi. Nowe eventy lądują chyba na końcu listy, więc dowolny event, np kliknięcie, będzie musiał poczekać, aż wykonają się wszystkie zadania, które były w kolejce, gdy był do niej dodawany.

EDIT:
W sumie można skorzystać też z invokeAndWait() i wtedy raczej nie zaspamujemy tak bardzo kolejki do EDT.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 3x, ostatnio: Wibowit
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)