Показаны сообщения с ярлыком tk. Показать все сообщения
Показаны сообщения с ярлыком tk. Показать все сообщения

9 дек. 2012 г.

Tcl/Tk: встраивание Tcl-консоли в приложение Tk

Ссылки

Есть программа tkcon, которая реализует продвинутую консоль Tcl, с историей, автодополнением и подсветкой синтаксиса. Ее автор, Jeffrey Hobbs (соавтор книги Practical Programming in Tcl/Tk), поддерживает также пакет megawidget, в который включен виджет Console, основанный на tkcon.

Пакет разрабатывается вместе с tcllib, но распространяется отдельно. Скачать последнюю версию можно здесь (на данный момент это widget-20100219). Этот виджет можно использовать, чтобы предоставить пользователю возможность выполнять Tcl-команды в контексте интерпретатора своего приложения.

Пример



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#! /usr/bin/wish

lappend auto_path /path/to/megawidget/library

package require Console

entry .myentry -highlightcolor red -highlightthickness 2

console .myconsole -setgrid 0 -showmenu false

pack .myentry .myconsole -expand true -fill both

Примечания


Использование в Tcl 8.6

В Tcl 8.6 на данный момент (стабильного релиза еще не было) сломана обратная совместимость в обработке виртуальных событий. Из-за этого в Console не работают некоторые биндинги, в том числе клавиши "вверх" и "вниз" для навигации по истории.

См. Bug #3582795. Автор виджета в курсе событий и написал мне, что пока что не ясна причина поломки совместимости в Tcl, поэтому исправления еще не внесены. Есть также workaround, который исправляет работу клавиш "вверх" и "вниз".


diff -rup widget-20050712.orig/library/console.tcl widget-20050712/library/console.tcl
--- widget-20050712.orig/library/console.tcl 2005-07-13 10:05:18.000000000 +0400
+++ widget-20050712/library/console.tcl 2012-11-30 22:57:16.590578106 +0400
@@ -1597,20 +1597,24 @@ bind Console <<Console_KillLine>> {
     }
 }
 bind Console <<Console_Clear>> [namespace code { _clear [winfo parent %W] }]
-bind Console <<Console_Prev>> [namespace code {
-    if {[%W compare {insert linestart} != {promptEnd linestart}]} {
- tkTextSetCursor %W [tkTextUpDownLine %W -1]
-    } else {
- _event [winfo parent %W] -1
-    }
-}]
-bind Console <<Console_Next>> [namespace code {
-    if {[%W compare {insert linestart} != {end-1c linestart}]} {
- tkTextSetCursor %W [tkTextUpDownLine %W 1]
-    } else {
- _event [winfo parent %W] 1
-    }
-}]
+foreach key [event info <<Console_Prev>>] {
+ bind Console $key [namespace code {
+     if {[%W compare {insert linestart} != {promptEnd linestart}]} {
+   tkTextSetCursor %W [tkTextUpDownLine %W -1]
+     } else {
+  _event [winfo parent %W] -1
+     }
+ }]
+}
+foreach key [event info <<Console_Next>>] {
+ bind Console $key [namespace code {
+     if {[%W compare {insert linestart} != {end-1c linestart}]} {
+  tkTextSetCursor %W [tkTextUpDownLine %W 1]
+     } else {
+  _event [winfo parent %W] 1
+     }
+ }]
+}
 bind Console <<Console_NextImmediate>> [namespace code {
     _event [winfo parent %W] 1
 }]

Опция setgrid

При использовании Console с опцией setgrid (которая включена по-умолчанию) внутри panedwindow можно наткруться на Bug #3466588, из-за чего, в частности, нельзя развернуть окно полностью.

Чтобы избежать этого эффекта, достаточно создать консоль с опцией -setgrid false.

7 дек. 2012 г.

Tcl/Tk: встраивание окна внешнего приложения

Ссылки

В Tk есть несколько способов встроить окно внешнего приложения в виджет.

Способ #1


Использовать frame с опцией -container.
Приложению нужно передавать window-id контейнера.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#! /usr/bin/wish

wm geometry . 400x400+100+100

frame .container -container yes

frame .other_frame -bg blue

pack .container .other_frame -fill both -expand 1

set container_window_id [scan [winfo id .container] %x]

exec ./embedded_window $container_window_id &  

Способ #2


Использовать blt::container.
Приложению нужно передавать window-id контейнера.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#! /usr/bin/wish

package require BLT

wm geometry . 400x500+100+100

blt::container .container

frame .other_frame -bg green

pack .container .other_frame -expand 1 -fill both

set container_window_id [scan [winfo id .container] %x]

exec ./embedded_window $container_window_id & 

.container configure -name embedded_window


Способ #3


Использовать blt::container совместно с  TkXext.
В этом случае можно встраивать даже те приложения, которым нет возможности передать window-id.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /usr/bin/wish

lappend auto_path /path/to/TkXext

package require TkXext
package require BLT

wm geometry . 400x500+100+100

blt::container .container

frame .other_frame -bg red

pack .container .other_frame -expand 1 -fill both

exec ./embedded_window &

set child_window_id [TkXext.find.window embedded_window]

after 1000 {
    TkXext.reparent.window $child_window_id [winfo id .container]
    .container configure -window 0x$child_window_id
}


Тестовое приложение


Ниже приведен код программы, с помощью которой можно проверить, корректно ли передаются события изменения размера окна и нажатия клавиши при встраивании.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/*
 * Compile: gcc embedded_window.c -o embedded_window -lX11
 * 
 */
#include <X11/Xlib.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv)
{
    Display *display = XOpenDisplay(NULL);
 
    int screen = DefaultScreen(display);
    
    Window parent_window = RootWindow(display, screen);

    if (argc == 2)
        parent_window = atol(argv[1]);

    Window child_window = XCreateSimpleWindow(display, parent_window,
                                              0,
                                              0,
                                              100,
                                              40,
                                              1,
                                              BlackPixel(display, screen),
                                              WhitePixel(display, screen));
    
    XSelectInput(display, child_window, KeyPressMask | ExposureMask);
    XMapWindow(display, child_window);
    
    XStoreName(display, child_window, "embedded_window");
    
    for (;;)
    {
        XEvent event;
        XNextEvent(display, &event);

        if (event.type == KeyPress)
        {
            printf("KeyPress\n");
        }
        
        if (event.type == Expose)
        {
            XWindowAttributes attrs;
            XGetWindowAttributes(display, child_window, &attrs);
                
            XClearWindow(display, child_window);
            
            XDrawLine(display, child_window, DefaultGC(display, screen), 0, 0, attrs.width, attrs.height);
            XDrawLine(display, child_window, DefaultGC(display, screen), 0, attrs.height, attrs.width, 0);
        }
    }
 
    XCloseDisplay(display);
    
    return 0;
 }