Multiple instances of window in perl, using gtk builder - perl

To get started, I am inexperienced scripting in perl, or using gtk, but I've been googling and researching how to for the past two or so weeks. It was difficult just figuring out where I could find the PMs for gtk on windows, and then even more so getting it to some semblance of 'working'. However, there have of course still been problems.
Skipping the above, I have two problems. For a slight bit of relevant background, I am trying to port an mirc script over to xchat, but to do that I obviously need to learn a whole 'nother language.. but anyway. The two problems are thus:
The window consists of several buttons, labels, and text areas. However, the window is.. 'frozen' in time unless one clicks on the title bar and holds. Clicking a button does nothing, not even to show it has been clicked, unless of course the title bar is clicked and held.
I have no idea how to initialize multiple instances of the same window. I have of course tried researching, but it's either not out there or I just haven't found it yet. To be specific.. My mirc script requires that multiple instances be allowed to exist, but I need the buttons for the specific instance to only affect that instance.. and so on.
In regards to problem 1, I do not know if the .xml glade file is important, so I won't post it immediately. I will however post the code that calls it:
my $glade_file = "window3.xml";
my $glade = Gtk2::Builder->new();
$glade->add_from_file($glade_file);
sub charopen {
my $window = $glade->get_object('window1');
$glade->connect_signals(undef, $window);
my $hp_cur = $glade->get_object('HP_Cur');
$window->set("title"=>$_[0][1]);
$hp_cur->set("label"=>$ini->val($_[0][1],"HPC"));
$window->show();
}

Graphical interface design relies on event processing. To work properly, it is important to reserve a thread to process user events (keyboard, mouse clicks...). That is the aim of calling Gtk2->main() when user interface is ready to accept user interaction.
To make the event thread exiting the event loop, an event callback method may invoke Gtk2->main_quit()
The Gtk2::Builder creates Gtk widget hierarchy from XML. To get multiple instance of the same window, you have to create a builder for each one.
Then your event callback methods has to get information about which window has sent the event, and the $user_data parameter may be used in that aim.
Here is a code proposal with a simple button click callback which use Perl reference to a hash so you can pass as many information as you want between window creator code and event callbacks:
sub createWindow($)
my $windowTitle = $_[0];
my $windowBuilder = Gtk2::Builder->new();
$windowBuilder->add_from_file($glade_file);
my $window = $windowBuilder->get_object('window1');
my $hp_cur = $windowBuilder->get_object('HP_Cur');
# Create hash with data (alternative: use Class::Struct for better code)
my %window_user_data = {
"title" => $windowTitle,
"window" => $window,
"hp_cur" => $hp_cur };
# Pass hash reference as user data
$windowBuilder->connect_signals(\%window_user_data);
# prepare interface: set data model into view and then...
$window->show();
}
# Click callback method defined on a button in window
sub button_click_callback($$) {
my $button = $_[0];
my $window_user_data_ref = $_[1];
# get back data model from view
print "Click received from button on "
. $window_user_data_ref->{"title"} . "\n";
}
There is another way to handle callbacks per window but it requires more Perl skills: you can design a Perl package to create an object instance for a specific window, and use $windowbuilder->connect_signals ($user_data, $windowcallbackinstance). In that case, such an object is called controller, and you have built your graphical interface based on Model-View-Controller (MVC) pattern.

Related

Application not detecting input language changes via Text Service Framework DLL

OK, I have been at this for a while ...
I am trying to track when user changes input languages from Language Bar.
I have a Text Service DLL - modeled from MSDN and WinSDK samples - that registers fine, and I can use the interfaces ITfActiveLanguageProfileNotifySink & ITfLanguageProfileNotifySink and see those events just fine.
I also have finally realized that when I change languages these events occur for the application/process that currently has focus.
What I need to do is to simply have these events able to callback to my own application, when it has the focus. I know I am missing something.
Any help here is appreciated.
Thanks.
I did some double-checking, and you should be able to create a thread manager object without implementing ITextStoreACP so long as you don't call ITfThreadMgr::Activate.
So, the code should look like:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
ITfThreadMgr* pThreadMgr(NULL);
hr = CoCreateInstance(CLSID_TF_ThreadMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, (LPVOID*) &pThreadMgr);
if (SUCCEEDED(hr))
{
ITfSource *pSource;
hr = pThreadMgr->QueryInterface(IID_ITfSource, (LPVOID*)&pSource);
if(SUCCEEDED(hr))
{
hr = pSource->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,
(ITfActiveLanguageProfileNotifySink*)this,
&m_dwCookie);
pSource->Release();
}
}
}
Alternatively, you can use ITfLanguageProfileNotifySink - this interface is driven from the ItfInputProcessorProfiles object instead of ItfThreadMgr. There's a sample of how to set it up on the MSDN page for ItfLanguageProfileNotifySink.
For both objects, you need to keep the source object (ITfThreadMgr or ITfInputProcessorProfiles) as well as the sink object (what you implement) alive until your application exits.
Before your application exits, you need to remove the sink from the source object using ITfSource::UnadviseSink, and then release the source object (using Release). (You don't need to keep the ItfSource interface alive for the life of your application, though.)

Notification window with buttons in Linux

I have a Perl script which listens to a port and filters messages, and, based on them, proposes to take action or ignore event.
I'd like to make it show a notification window (not a dialogue window) with buttons 'take action' and 'ignore', which would go after a certain timeout.
So far I have something like this:
my #react = ("somecommand", "someoptions); # based on what regex a message matched
my $cmd = "xmessage";
my $cmd_args = "-print -timeout 7 -buttons React,Dismiss $message"; # raw message from port
open XMSG, "$cmd $cmd_args |";
while (<XMSG>) {
if ($_ eq "React\n") {
do something...
}
}
But it would handle only one notification at once, and the next message would not appear until the previous one is dismissed, reacted to or timed out, so it's quite a bad decision. I cannot do anything until I get return code from xmessage, and I can't get xmessage run a command. Well I probably can if I introduce event IDs and listen to a socket where xmessage prints, but it would make things too complicated, I guess.
So I wonder is there a library or an utility for Linux to draw notify-like windows with buttons which would each trigger a command?
I'm sorry I didn't see this one when it first was posted. There are several gui toolkits which could do something along these lines. Prima is a toolkit built especially for Perl and has no external library dependencies.
For when you just need a popup dialog, there is the Ask module which delegates the task of popping up windows to any available library.
In case anyone's interested, I've ended up writing a small Tcl/Tk program for that, the full code (all 48 lines) can be found here: http://cloudcabin.org/read/twobutton_notify, and you can ignore the text in Russian around it.

Some beginner questions

I guess I'll start by saying I am very new to B4A, and to programming in general. I have some very basic java and html exp. but that's it. I do not have any basic4ppc or really any IDE experience. Been using B4A for a few days now and can't get over the hump. Here are my noob questions:
Does having many activities (20-30+) slow down the app? Is there a downside to having a lot of activities?
I can't figure out how to scroll in the designer. I am trying to make a screen that has 25 buttons down in 1 column. However I can't scroll down to add more buttons below. I am able to add buttons programmically and in the fashion that I want (using a for loop), but is it normal to create views at runtime like this?
How do you ensure your app looks the same across all devices? Tablets? I have a scroll view that fits perfect in the emulator, but on my phone (droid x), the bottom of the scroll view is not stretched to the bottom of the phone. I use the code: scvScreen1.Initialize(100%y). Is that not right?
I have a Email screen in which is comprised of an edittext and a Send button, so that the users can send me questions from the app. However the Send button gives me this error on the 'URI =' line: "LastException java.lang.NumberFormatException: mailto:" here is the code:
Sub btnSendEmail_Click
Dim Uri As String
Uri="mailto:me#gmail.com?subject=Test Email&body=" + edtHelpEmail.Text
Dim Intent1 As Intent
Intent1.Initialize(Intent1.ACTION_VIEW,Uri
StartActivity(Intent1)
End Sub
Or is there another way to open the device's default email program?
Regarding last question, how do I copy error messages to clipboard?? I selected the red error message on the bottom right of the IDE and tried ctrl-c, but didn't work.
In B4A, what is a good method of storing persistent data? All I really need to store are some strings. Nothing fancy. These strings are to be stored locally. AI made this easy by using TinyDB.
When using the designer, how do you ensure your views are centered on all devices? For instance, I have a screen that has several rows made up of: (label, edittext, label). And I want each row to be center aligned. Do I do this programmically? I'm thinking I would have to append each row of (label, edittext, label) to a panel, then in the code center the panel. Is this correct?
That's all I got for now, but I'm sure there will be plenty more questions later.
1) The whole idea of android is to small components i.e. Apps working together, so no need to worry about opening lots of activities. Memory is very well managed behind the scenes in Android.
2) Sure. That sounds fine to me. Use the Layout designer as much as you can and then add the dynamic stuff later. It's all about striking a balance between the size of your code and the number of activities.
3) In the Designer there's an option called 'Send to UI Cloud'. This compares your app over multiple screen sizes. You can also scale your design and programmatically resize specific controls within your app in the Activity_Create Lifecycle
4) What you're doing is almost correct. I corrected your code:
Sub MailTo(StrAddress As String, StrSubject As String, StrBody As String)
Dim StrMethod As String = "Sub MailTo(StrAddress As String, StrSubject As String, StrBody As String)"
Try
Dim StrUri As String
StrUri = "mailto:" & StrAddress & "?subject=" & StrSubject & "&body=" & StrBody
Dim Intent As Intent
Intent.Initialize(Intent.ACTION_VIEW, StrUri)
StartActivity(Intent)
Catch
If BlnLoudExceptions Then CdException.Show(StrClass, StrMethod, LastException)
End Try
End Sub
I tend to have a code module called CdIntent.bas for these functions as it both keeps the project organised and makes it easier to implement the same functionality across projects.
Then to call you would use
CdIntent.MailTo("me#yes.no", "Subject!", "Body!")
5) I have a file called CdException.bas
Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
End Sub
Sub Show(StrClass As String, StrMethod As String, Ex As Exception)
LogColor("Exception: " & Ex.Message & " - Class: " & StrClass & " - Method: " & StrMethod, Colors.Magenta)
End Sub
and then wrap functions in the following way:
Sub FunctionName(...Parameters...) as Int
Dim StrMethod As String = "Sub Sleep(LngMilliseconds As Long)"
Dim IntResult As Int = 0
Try
[code here inc. IntResult = ???]
Catch
If BlnLoudExceptions Then CdException.Show(StrClass, StrMethod, LastException)
End Try
Return IntResult
End Sub
BlnLoudExceptions is a global boolean that you'd declare in
Process_Globals that you can switch on an off exception logs.
StrClass is a global String that you'd declare in Process_Globals
that contains the name of the class e.g. "CdIntent.bas"
The exceptions then appear in magenta in the log screen along with the method name and class in which they occurred allowing you to home in on them.
6) I have a table in an SQLLite database called TabletSettings, which has two TEXT colums called 'Name' and 'Value'. It works well and gets you into a (what I think is a) good habit of always having a database available to your app from the get-go.
7) I'll get back to you on this as I haven't done this before.
Until then, the following thread will help you in the B4A forum http://www.basic4ppc.com/android/forum/threads/convert-integer-to-dip.18800/
I agree with Jim's point but will attempt to answer 1.
I'm new to android myself but as I understand it activities on the whole are only running when active. Unless you are using the app to continuously do something there is only one activity at a time. The number of activities is likely to affect the ram available more than anything. Lastly it might be worth walking first rather than running so to speak but trying a single and then add multiple activities.
You could try adding a ListView or ScrollView where the items are the buttons, this seems to be the std way of doing things otherwise a tabbed view.

pause viewmodel process for user input

I've been looking at a view examples of the typical "raise dialog from viewmodel" problem, noting 3 main solutions:
use attached behaviors
use a mediator pattern
use a service
I'm getting a bit bogged down though and struggling to find a solution that easily fits into my problem space - which is a very simple file copy problem:
My viewmodel is processing a loop (copying a list of files)
When a file already exists at the destination I need to raise a modal dialog to get confirmation to replace
The vm needs to wait for and receive confirmation before continuing
The "modal dialog" is actually not a new window but a hidden overlay in my MainWindow, as per http://www.codeproject.com/KB/WPF/wpfmodaldialog.aspx (thanks Ronald!)
I'm mostly there but the biggest struggles I have are:
- how to pause the loop in the viewmodel while it waits for input
- how to get input back to the viewmodel within the loop so it can carry on
So far I'm leaning towards the service solution because it seems a direct method call with a return that the vm must wait for. However, it does mean the service needs to tie directly to the view in order to make an element visible?
If anyone can post some simple code that deals directly with this problem I (and the net) would be very happy! Thanks!
For example, you have a service called IDialogService with the following interface:
public interface IDialogService
{
bool ConfirmAction(string title, string confirmationText);
}
As you mentioned, in order for the service to be able to show the actual dialog it needs to have a reference to the view that will show the actual overlay element. But instead of directly referencing the view I prefer to reference it via an interface. Lets call it ICanShowDialog and it will have the following members:
public interface ICanShowDialog
{
void ShowDialog(object dialogContent);
void HideDialog();
}
This interface will be implemented by your view that owns the dialog overlay (e.g. your main window).
Now the interesting part: suspending the code execution while the dialog is shown. First of all, I would recommend you not to use overlay elements but use usual windows if possible. Then you will not have that problem. You can style the dialog window so it will look just like the overlay element.
Anyway, if you still want to use overlay elements then you can do the following trick to suspend the code execution:
Here is pseudo code of the ConfirmAction method of the IDialogService inteface:
public bool ConfirmAction(string title, string confirmationText)
{
ConfirmationDialogView dialogView = new ConfirmationDialogView(title, confirmationText);
DialogShower.ShowDialog(dialogView); // DialogShower is of type ICanShowDialog
while (!dialogView.ResultAvailable)
{
DispatcherUtils.DoEvents();
}
DialogShower.HideDialog();
return dialogView.Result;
}
Here is the code of DispatcherUtils.DoEvents() (that was taken from here: http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html):
public static class DispatcherUtils
{
public static void DoEvents()
{
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(
DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
}
But I must warn you. Using DoEvents can result in some subtle bugs caused by inner dispatcher loops.
As an alternative to suspending the code execution while a dialog is shown you can use callbacks:
public interface IDialogService
{
void ConfirmAction(string title, string confirmationText, Action<bool> dialogResultCallback);
}
But it will not be so convenient to use.

Signal fires twice from gtkmm popup list

It's been a while since I used GTK+, and the last time I did was in C, not using gtkmm and C++ as I am now. Anyway, I have what I think should be an easy problem to solve:
I have a pop-up menu consisting of a list of radio buttons, and when I click one of them I want some action to occur. The code goes like this:
Gtk::RadioMenuItem::Group group;
for ( size_t i = 1; i < LH_MAX; ++i )
{
Gtk::RadioMenuItem* pItem = new Gtk::RadioMenuItem( group, names[i], names[i] );
pItem->set_name( names[i] );
pItem->signal_activate().connect( sigc::mem_fun(*this, &MyClass::on_item_activated) );
pItem->show();
m_Menu.append( *Gtk::manage(pItem) );
}
The only problem I see is that MyClass::on_item_activated gets called twice when a previously-unselected radio button is chosen from the menu. It's called only once when the already-selected radio button is clicked.
I'm guessing that the first firing is to say "something is no longer activate," and the second is for the new radio button activation. Whether I'm right or wrong, the question is the same: how best can I have my handler only take action once per click? Either I need the handler to get called only once, or I need something to check from inside it to know if the callback is a "duplicate" or not.
You could use sigc::bind to supply the item as a argument to the callback function.
pItem->signal_activate().sigc::bind(sigc::mem_fun(*this,&MyClass::on_item_activated),pItem));
Then you can use item->get_active() in the callback to respond to activations only.
void MyClass::on_item_activated(Gtk::RadioMenuItem* item) {
if (item->get_active()) {
// Do some stuff
}
}
That's what I do too, connect to signal_toggled() and check if get_active() is true.
I don't know exactly what you're trying to accomplish (or what MyClass is and what base classes it inherits from), but connecting to signal_toggled() might be more useful than signal_activate()
/Agree with Johannes. Check if the item is activated when receiving the signal.