Home | Download | Discussion | Help | Site Map | New Posts | Sign in

Latest Site News

MTS Movie Night #2 - posted on 10th Aug 2017 at 2:58 PM
Replies: 15 (Who?), Viewed: 1868 times.
Top Secret Researcher
Original Poster
#1 Old 31st Jan 2015 at 6:12 AM Last edited by scumbumbo : 20th Mar 2015 at 5:36 AM.
Default OK/Cancel and User Input via Python
The OK/Cancel dialogs were easy, but I couldn't seem to get the user text input dialogs working from Python. I couldn't seem to get the tuning generated right via scripting, and the XML tunings supported in interaction resources are all very specific in what they do. I finally had the brilliant (??obvious??) idea to just create a completely new class for it based off the original UiDialogTextInput class in ui_dialog_generic.py that skipped the tuning for the input items.

Edit See below for a better text input method that doesn't require the new class hack! /Edit

Amazingly, it finally started working (sort of). After finding I couldn't get the typed text back out again from the callback, I did more hacking. It seems the on_text_input required some changes as the text_inputs didn't exist (those were the tuning items I couldn't get working). Also, the only text input options I could get working were the name of the input field and the min and max input length (commented out, just edit them if you want them). But that's really enough to do most everything a mod might need.

Two commands are in the test script I've uploaded: dialogtest.okcancel and dialogtest.input

Enjoy! If anybody can figure out how do to the tuning properly to just use the original UiDialogTextInput that'd be purely awesome -- it appears to support multiple text inputs in a dialog, but I'm not sure -- it certainly didn't work to call msg.text_input.add() multiple times in my hacked class, but the original does call that in a loop in the build_msg() method.
Download - please read all instructions before downloading any files!
File Type: rar DialogTest.rar (2.6 KB, 38 downloads) - View custom content
Description: Dialog tests for OK/Cancel and Text Inputs
Advertisement
Top Secret Researcher
Original Poster
#2 Old 5th Mar 2015 at 5:15 PM
Update - I've figured out how to support localized strings (including inserting variable values - numbers, money and raw strings) via the scripts now. This is used in SimLotto v1a, so there's no real need to post an example here. It was actually pretty simple once I sat down to make the attempt it all fell together pretty quickly.
Top Secret Researcher
Original Poster
#3 Old 20th Mar 2015 at 5:33 AM Last edited by scumbumbo : 20th Mar 2015 at 5:54 AM. Reason: fixed some minor typos
Update 2... I've figured out how to do the dialog using the base game's UiDialogTextInputOkCancel instead of creating a new class for the dialog like I did in the first example. The trick is to generate the basic tuning variables in code. This code is HEAVILY commented as I'm hoping maybe someone can figure out how to extend this to generate an input dialog with TWO text boxes.

I've also included the code I used to override the build_msg in the UiDialogTextInput class to figure out how to generate the required tuning on the fly. It's commented out as it WILL hang the game if you run an interaction that displays a dialog with two input boxes (e.g. write a book which has both a "title" and "description" entry).

I can't put code or URLs on the EA forums, so if someone wants to post this to EA for possibly letting us in on how to get TWO input boxes working... well, let's just say I wouldn't scream "FOUL"

This is a raw script, so you'll need to put it in a Mods\SOMEFOLDER\Scripts folder, but if you don't know that you probably should NOT be playing with this!

Edit: I posted a discusson on the EA forums, so maybe it will get some visibility there by someone with more PythonFu than I....
Download - please read all instructions before downloading any files!
File Type: rar text_input_v2.rar (2.8 KB, 25 downloads) - View custom content
Top Secret Researcher
Original Poster
#4 Old 20th Mar 2015 at 6:02 AM
Quote:
Originally Posted by scumbumbo
I've also included the code I used to override the build_msg in the UiDialogTextInput class to figure out how to generate the required tuning on the fly. It's commented out as it WILL hang the game if you run an interaction that displays a dialog with two input boxes (e.g. write a book which has both a "title" and "description" entry).

If you want to play with this override to log the tuning variables but don't want to write your own logging function, I'll share my ScumLog.py file.
Download - please read all instructions before downloading any files!
File Type: rar ScumLog.rar (252 Bytes, 12 downloads) - View custom content
Top Secret Researcher
Original Poster
#5 Old 21st Mar 2015 at 1:17 AM
Quote:
Originally Posted by scumbumbo
I posted a discusson on the EA forums...

And already got an answer from SimGuruEugi - to get a dialog with two text inputs, you just can't have an icon on the dialog. It was so simple I could kick myself! I tried it out (remove the icon_override info from the show_dialog call) and it worked out fine!
Top Secret Researcher
Original Poster
#6 Old 1st Apr 2015 at 7:02 AM
EA changed the ui_dialog_generic code for dialog inputs which required a simple change to the dialog geneating code. Instead of using a basic dict for the text_inputs passed to the dialog, you need to use a dictionary object. I used an AttributeDict from the sims4 collections, but probably a named tuple or similar could be used. Interested parties can check version 5a of the Change Sim Name script.
Top Secret Researcher
Original Poster
#7 Old 22nd Apr 2015 at 7:23 AM
Default And Now the SimPicker....
Solved the last issue I was having to generate a sim picker from script. Here's the basic code....
Code:
import sims4.commands
import sims.sim_info
import services
from sims4.localization import LocalizationHelperTuning
from ui.ui_dialog_picker import SimPickerRow, UiSimPicker
import sims4.collections

@sims4.commands.Command('pickertest', command_type=sims4.commands.CommandType.Live)
def sim_picker_dialog_test(_connection=None):
    output = sims4.commands.CheatOutput(_connection)
    client = services.client_manager().get_first_client()

    def get_inputs_callback(dialog):
        if not dialog.accepted:
            output("Dialog was closed/cancelled")
            return
        output("Dialog was accepted")
        for sim_id in dialog.get_result_tags():
            output("id={}".format(sim_id))

    localized_title = lambda **_: LocalizationHelperTuning.get_raw_text("Sim Picker Dialog Test")
    localized_text = lambda **_: LocalizationHelperTuning.get_raw_text("Select up to five sims and press OK or close dialog....")
    max_selectable_immutable = sims4.collections.make_immutable_slots_class(set(['multi_select', 'number_selectable', 'max_type']))
    max_selectable = max_selectable_immutable({'multi_select':False, 'number_selectable':5, 'max_type':1})
    dialog = UiSimPicker.TunableFactory().default(client.active_sim, text=localized_text, title=localized_title, max_selectable=max_selectable, min_selectable=1, should_show_names=True, hide_row_description=False)
    for sim_info in services.sim_info_manager().get_all():
        # Set second arg below to True to have that sim preselected/highlighted....
        dialog.add_row(SimPickerRow(sim_info.sim_id, False, tag=sim_info.sim_id))
    dialog.add_listener(get_inputs_callback)
    dialog.show_dialog(icon_override=(None, sim_info))

Should be simple enough to follow if you've understood the code from the previous examples. This will generate a sim picker with all sims, allow up to five to be selected, and output the sim ids of the selected sims to the cheat console. The localized_title and text should be able to support internationalization using the same methods I used for the dialogs in SimLotto.

As always... enjoy and hope this helps out other scripters!

ETA: You'll probably want to use a different sim for the icon_override than just the last sim that got added to the picker like I did - bit of a shortcut for testing
Test Subject
#8 Old 29th Jul 2015 at 10:31 PM
Any chance you would bother writing a generic choice picker (I can supply any sort of text strings as choices) as a code example?
Top Secret Researcher
Original Poster
#9 Old 30th Jul 2015 at 5:40 AM
Quote:
Originally Posted by blubber
Any chance you would bother writing a generic choice picker (I can supply any sort of text strings as choices) as a code example?

I think @Deaderpool was looking into whether something like that was possible to use for configuration choices for MC Command Center. Not sure what he found out, but last I heard it wasn't promising.
Mad Poster
#10 Old 30th Jul 2015 at 1:10 PM
No, it was not. Basically, most of the dialogs built-in are pretty hard-wired to do pretty specific things and the meat of the code for the dialogs is on the C++ side. It's like they initialize the dialogs from the python side, and the dialogs themselves are in Flash, but a lot of the actual population of the dialogs happens in C++ and you can't really finesse it in any way.

One thing I was thinking of trying, but it'll be a while before I get the time to really dig-into it, is to write my own dialog in Flash as a new GFX resource. I think I could then call that from Python sending it the data I want to populate the screen with and have it work however I want it to work. It'd be a bit of work, I'm thinking, because I'd also want localization to work so it'd have to tie-in to the C++ code for the same functionality there as what we have in existing dialogs.

Anyways, I think that's how you have to do it for the more specific dialogs like what we're needing.
Top Secret Researcher
Original Poster
#11 Old 30th Jul 2015 at 3:53 PM
Quote:
Originally Posted by Deaderpool
No, it was not. Basically, most of the dialogs built-in are pretty hard-wired

One thing I was thinking of trying, is to write my own dialog in Flash as a new GFX resource.

Doesn't sound like a fun project. Might be worth dropping a query on EA forums to SGMS to ask if that even sounds like it would work, they may be able to supply some advice to make it easier or (more likely) tell you why it wouldn't work easily.
Test Subject
#12 Old 4th Aug 2015 at 9:43 PM Last edited by maybecats : 4th Aug 2015 at 10:44 PM.
I wrote something up which uses buttons instead of a scrollable list. It does limit the possible entries, but for shorter choice lists (or if you code it to use some "More..." button at the end) it's perfectly fine and comfortable to use as an end-user.

maybecats_choices.py:
Code:

import injector
import services
import traceback
from server_commands.argument_helpers import\
    OptionalTargetParam, get_optional_target
from sims4.localization import LocalizationHelperTuning
from ui.ui_dialog import ButtonType, UiDialog, UiDialogResponse
import ui.ui_dialog_service

class UiDialogChoicesInput(UiDialog):
    __qualname__ = 'UiDialogChoicesInput'
    CHOICES_LOWEST_ID = 20000

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._choice_responses = list()

    def _get_responses_gen(self):
        # yield old values:
        super_results = list(super()._get_responses_gen())
        for result in super_results:
            yield result
        # yield our choices:
        for response in self._choice_responses:
            yield response

shown_choices_dlg = None

@injector.inject_to(ui.ui_dialog_service.UiDialogService, "dialog_respond")
def display_choices_dialog_respond_hook(original, self, *args, **kwargs):
    try:
        dialog = self._active_dialogs.get(\
            args[0], None)

        global shown_choices_dlg
        if shown_choices_dlg != None:
            dlg = shown_choices_dlg
            shown_choices_dlg = None
            dlg.respond(args[1])
            return True

        # regular handling of other dialogs:
        result = original(self, *args, **kwargs)
        return result
    except Exception as e:
        print("INTERNAL ERROR ON RESPOND: " + str(e))
        print(traceback.format_exc())

def display_choices(choices, choice_callback, title="Choose...", \
        text="Please make a choice", icon_sim=None):
    global shown_choices_dlg

    # create dialog:
    client = services.client_manager().get_first_client()
    dlg = UiDialogChoicesInput.TunableFactory().\
        default(
        client.active_sim,
        text=lambda **_: LocalizationHelperTuning.get_raw_text(text),
        title=lambda **_: LocalizationHelperTuning.get_raw_text(title)
        )

    # add choices:
    choice_id = dlg.CHOICES_LOWEST_ID
    i = 0
    while i < len(choices):  # NOT a for loop to require indexing to work
        choice = choices[i]
        dlg._choice_responses.append(\
            UiDialogResponse(dialog_response_id=choice_id,
                text=lambda _txt=choice, **_: \
                    LocalizationHelperTuning.get_raw_text(\
                    _txt),\
                ui_request=UiDialogResponse.UiDialogUiRequest.NO_REQUEST))
        choice_id += 1
        i += 1
    # default cancel choice:
    dlg._choice_responses.append(\
        UiDialogResponse(dialog_response_id=ButtonType.DIALOG_RESPONSE_CANCEL,\
            text=lambda **_: LocalizationHelperTuning.get_raw_text(\
                "Cancel"),\
            ui_request=UiDialogResponse.UiDialogUiRequest.NO_REQUEST))

    # response handler calling the choice_callback of the user:
    def response_func(dialog):
        try:
            if dialog.accepted:
                try:
                    choice_callback(choices[dialog.response - \
                        dlg.CHOICES_LOWEST_ID])
                except IndexError:
                    choice_callback(None)
            else:
                choice_callback(None)
        except Exception as e:
            print("[maybecats choices] error in choice_callback: " + str(e))
            print(traceback.format_exc())
            raise e  # propagate error

    # show dialog:
    dlg.add_listener(response_func)
    if icon_sim == None:
        icon_sim = client.active_sim
    shown_choices_dlg = dlg
    dlg.show_dialog(icon_override=(None, icon_sim))
 


Use it like this:
Code:

from maybecats_choices import display_choices

def show_my_choices():
    def handle_result(result):
        if result == None:
            print("The user clicked cancel!"
        elif result == "Choice A":
            print("The user picked A")
        elif result == "Choice B":
            print("The user picked B")
    display_choices(["Choice A", "Choice B"], handle_result, text="So you gotta pick A or B....", title="Life Decision!")


Feel free to report bugs and improvements, e.g. to get rid of that sorta-ugly global var hack to recognize the dialog again which SHOULDNT be necessary, but for some reason it appears to be.
Top Secret Researcher
Original Poster
#13 Old 5th Aug 2015 at 1:56 AM
Quote:
Originally Posted by maybecats
I wrote something up which uses buttons instead of a scrollable list.

Awesome! Totally awesome!

For those wondering what this ends up looking like (I got up to choice I before I got bored with trying one more button to see if it would take, lol)....
Screenshots
Top Secret Researcher
Original Poster
#14 Old 5th Aug 2015 at 2:45 AM
Quote:
Originally Posted by maybecats
Feel free to report bugs and improvements, e.g. to get rid of that sorta-ugly global var hack to recognize the dialog again which SHOULDNT be necessary, but for some reason it appears to be.

I'm not sure if this is uglier or not, I think probably it is...

I couldn't figure out why dialog_show isn't setting the _active_dialog for this either, so I took a big hack at it:
Code:
    # show dialog:
    dlg.add_listener(response_func)
    if icon_sim == None:
        icon_sim = client.active_sim
    #shown_choices_dlg = dlg
    dlg_service = services.ui_dialog_service()
    dlg_service._active_dialogs[dlg.dialog_id] = dlg
    dlg.show_dialog(icon_override=(None, icon_sim))


Laugh away - this probably deserves it, but it worked!

ETA - And yes, this allows you to completely eliminate the dialog_respond hook. It's not a "proper" fix though, by any stretch of the imagination.
Mad Poster
#15 Old 5th Aug 2015 at 3:45 AM
Quote:
Originally Posted by scumbumbo
I'm not sure if this is uglier or not, I think probably it is...

I couldn't figure out why dialog_show isn't setting the _active_dialog for this either, so I took a big hack at it:
Code:
    # show dialog:
    dlg.add_listener(response_func)
    if icon_sim == None:
        icon_sim = client.active_sim
    #shown_choices_dlg = dlg
    dlg_service = services.ui_dialog_service()
    dlg_service._active_dialogs[dlg.dialog_id] = dlg
    dlg.show_dialog(icon_override=(None, icon_sim))


Laugh away - this probably deserves it, but it worked!

ETA - And yes, this allows you to completely eliminate the dialog_respond hook. It's not a "proper" fix though, by any stretch of the imagination.

Nice! It's a step above the default dialogs for sure. I could see using something like that rather than having to have multiple menu choices on a Sim or computer for something (ie. Choose Gender: Male or Female or Both). If I ever get new R&D time, I'll have to look at this more!
Test Subject
#16 Old 8th Aug 2017 at 2:47 PM
hi,how could i create a bigger ui dialog
Back to top